• トップ
  • ブログ一覧
  • Javaで簡単な英文自動要約(Automatic English Text Summarization)プログラムを作る
  • Javaで簡単な英文自動要約(Automatic English Text Summarization)プログラムを作る

    メディアチームメディアチーム
    2020.04.02

    IT技術

    Java で簡単な「英文自動要約プログラム」を作る

    ニュースやオンライン資料の長い英文を読む時に、時間とそれなりの努力が必要になると思います。

    そしていざ読んでみると、自分の欲しい情報ではなく、「貴重な時間は無駄に使ってしまったな」と感じた事あるのでは?

    そんな時は「自動英文要約」があれば、役に立つのではないでしょうか!

    抽出的要約メソッド

    文章自動要約の種類は、おおむね2つのやり方で区別されます。

    それは「抽出的要約」と「生成的要約」です。

    今回は「抽出的要約」の方法で、シンプルな英文自動要約プログラムを作ってみたいと思います!

    必要なツールとライブラリ

    まず、必要なツールとライブラリを揃えましょう。

    英文の stopwordsファイル

    こちらはテキストファイルの形で、下記サイトよりダウンロードできます。

    【ダウンロードサイト】
    https://gist.github.com/sebleier/554280

    opennlp-tools-1.9.2.jar

    このライブライは jarファイルの形で、「Apache OpenNLP」のウェブサイトからダウンロード出来ます。

    下記の URL より、zipファイルをダウンロードしてください。

    「opennlp-tools-1.9.2.jar」は、この zipファイル内に含まれています。

    【ダウンロードサイト】
    https://www.apache.org/dyn/closer.cgi/opennlp/opennlp-1.9.2/apache-opennlp-1.9.2-bin.zip

    en-sent.bin とen-token.bin

    これらのファイルも、「Apache OpenNLP」のウェブサイトからダウンロード出来ます。

    【ダウンロードサイト】
    http://opennlp.sourceforge.net/models-1.5/

    JavaのIDE

    JavaのIDEとしては、「www.jetbrains.com」の「Intellij IDEAを使っています

    下のURLから、コミュニティ版が無料でダウンロードできます。

    【ダウンロードサイト】
    https://www.jetbrains.com/idea/download/

    Java は、Oracle版の JDK 8 を使用しています。

    直接、Oracle のダウンロードページからダウンロードして下さい。

    尚、Java のインストール方法や Intellij IDEA のインストールと使い方は、過去の記事などを参照にしていただければ!

    関連記事

    featureImg2019.03.20【初心者向け】Windows10にJavaをインストールする方法Javaをインストールしたい! にゃんこ師匠今日もプログラミング教室始めるぞ~いミツオカおい、ジジイ、そろそろJava...

    featureImg2020.03.03【初心者向け】MacにJava 11 をインストールする方法MacでJavaを始めようミツオカにゃんこ師匠!Mac に Java をインストールする方法を教えてください! にゃん...

    関連サイト

    【Intellij IDEA公式サイト】
    https://www.jetbrains.com/idea/

    【Intellij IDEA 日本の販売代理店】
    https://samuraism.com/jetbrains/intellij-idea

    コーディングをしてみる

    ツールが揃ったら、早速コーディングを始めましょう!

    今回の「シンプルな英文自動要約プログラム」は、5つの段階で作っていきます。

    ステップ1テキストからの文の抽出
    ステップ2ストップワードの削除
    ステップ3各文を構成する全て単語の取得
    ステップ4各単語への重み付け
    ステップ5各文のスコアの算出
    (Thresholdの値を定めて、その値を満たす文は要約文として選択します。)

    Javaプロジェクトの作成

    まず、「Intellij IDEA」を起動させて、新しい Javaプロジェクトを作って下さい

    プロジェクトの構造は、以下のようになっています。

    プロジェクトの構造

    プロジェクト名は 「english_text_summarizer」 と設定していますが、あくまでも自分のわかりやすい名前を設定していただければ。

    english_text_summarizer
    |_input //入力ディレクトリ
     |__statue_of_liberty.txt //入力テキストファリル
    |_lib //ライブラリのディレクトリ
     |__opennlp-tools-1.9.2.jar
    |_src //プログラムのソースコードディレクトリ
     |__opennlp.models
      |__en-sent.bin
      |__en-token.bin
     |__en_stopwords.txt
     |__TextSummarizer.java //シンプルな英文自動要約プログラムのソースコード

    ステップ1:テキストから文を抽出

    ステップ1:テキストから文を抽出」をしていきます。

    この段階では、文を抽出するメソッドを作って、テキストから文を抽出します。

    抽出した文は、インデックスをつけて「HashMap」に格納します。

    インプットとして、自由の女神に関する英文を採用します。

    こちらの URL よりコピー出来ます。

    【関連サイト】
    https://lingua.com/english/reading/statue-of-liberty/

    テキストファイルは、「statue_of_liberty.txt」と名前を付けて、「inputディレクトリ」に保存します。

    文の抽出

    例えば、以下のテキストから文を抽出すると…

    After touring the Statue of Liberty, Claire spent the rest of the day in New York City visiting other important monuments and historic landmarks. Claire left New York hoping to have had the time to explore more sites, but she can’t wait to return to the city in the future.

    次のような2つの文が得られます。

    1. After touring the Statue of Liberty, Claire spent the rest of the day in New York City visiting other important monuments and historic landmarks.
    2. Claire left New York hoping to have had the time to explore more sites, but she can’t wait to return to the city in the future.

    文の抽出をするメソッドのコード

    文を抽出するメソッドのコードは、下記のように書きます。

    メソッドに「detectSentences」と名付けました。

    1private void detectSentences(String fileName) throws IOException {
    2   InputStream is = new FileInputStream("src/opennlp/models/en-sent.bin");
    3   SentenceModel sentenceModel = new SentenceModel(is);
    4   SentenceDetectorME sentenceDetector = new SentenceDetectorME(sentenceModel);
    5
    6   String sentences[] = sentenceDetector.sentDetect(new String ( Files.readAllBytes( Paths.get(fileName))));
    7
    8   for(int i = 0; i < sentences.length; i++){
    9       sentenceMap.put(i, sentences[i]);
    10   }
    11}

    ステップ2:ストップワードの削除

    ステップ2:ストップワードの削除」を行います。

    ストップワード(stopwords)」とは、「助詞」や「非常に一般な単語で、文章の文脈にあまり影響を及ばない単語」のことを言います。

    このステップ2では、「en_stopwords.txt」のファイルに基づいて、ステップ1に得られた全ての文から、ストップワードを削除します。

    削除の例を挙げます。

    原文

    Claire left New York hoping to have had the time to explore more sites, but she can’t wait to return to the city in the future.

    ストップワードを除いた文

    Claire left New York hoping to have had the time to explore more sites, but she can’t wait to return to the city in the future.

    ステップ3:文のトークン化:文を構造する全ての単語を取得

    ステップ3:文のトークン化:文を構造する全ての単語を取得」します。

    文のトークン化は、英語で「tokenization」と言います。

    ステップ3では、テキストからストップワードを除いて、全ての単語を取り出します

    取得例はこちら!

    テキスト

    Claire left New York hoping to have had the time to explore more sites, but she can’t wait to return to the city in the future.

    トークン化

    Claire
    left
    New
    York
    hoping
    have
    had
    time
    explore
    sites
    she
    wait
    return
    city
    future

    ステップ4:得られた各単語に重み付け

    ステップ4:得られた各単語に重み付け」を行います。

    ステップ4では、得られた単語に重みをつけて、「HashMap」に格納します

    単語の重み付け

    ちなみに、単語の重みの付け方は色々ありますが、ここでは単純に文章内の各単語の頻度を使っています。

    これは 「word frequency」 と言います。

    トークン化の例を使うと、重み付けの単語を表にまとめると、こんな感じになります。

    単語頻度
    Claire1
    left1
    New1
    York1
    hoping1
    have1
    had1
    time1
    explore1
    sites1
    she1
    wait1
    return1
    city1
    future1

    コード

    ステップ2、3、4のコードはこちら!

    1private void readStopwords() throws IOException {
    2   BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src/en_stopwords.txt")));
    3   String line = null;
    4   while ((line = br.readLine()) != null){
    5       stopwordList.add(line);
    6   }
    7   br.close();
    8}
    9
    10private void calculateWordFrequency(SimpleTokenizer simpleTokenizer){
    11   for (Map.Entry<Integer, String> kv: sentenceMap.entrySet()){
    12       String tokens[] = simpleTokenizer.tokenize(kv.getValue());
    13       for(String currentWord:tokens){
    14           if (!stopwordList.contains(currentWord) && currentWord.length() >= 2){//stopwordsの削除
    15               int wordCount = wordFreqMap.containsKey(currentWord.toLowerCase()) ? wordFreqMap.get(currentWord.toLowerCase()) : 0;
    16               wordFreqMap.put(currentWord.toLowerCase(), wordCount + 1);
    17           }
    18       }
    19   }
    20}

    ステップ5:単語の重みを用いて、各文にスコアを与える

    ステップ5:単語の重みを用いて、各文にスコアを与える」を行います。

    最後の段階では、文に含まれる単語の重みを用いて、文のスコアを計算します

    つまり、単語の重みの合計は、文のスコアとなります。

    そして、文の「threshold」を決めて、これ以上のスコアを所有する文を抽出し、文章の要約として出力します。

    文の「Threshold」は、スコアの平均値+標準偏差から算出されます。

    文のスコア

    上述の例を使うと、文のスコアはこのような感じになります。

    単語頻度
    Claire1
    left1
    New1
    York1
    hoping1
    have1
    had1
    time1
    explore1
    sites1
    she1
    wait1
    return1
    city1
    future1

    文のスコア=15

    コード

    ステップ5のコードは、こちら!

    1//各文に含まれる単語の重みを足算し、その合計を該当文のスコアとする。
    2private void calculateSentenceScore(SimpleTokenizer simpleTokenizer){
    3   Double totalScore = 0.0;
    4   for(Map.Entry<Integer, String> kv: sentenceMap.entrySet()){
    5       String currentSentence = kv.getValue();
    6       String wordsInSetence[] = simpleTokenizer.tokenize(currentSentence);
    7       sentenceScoreMap.put(currentSentence, 0.0);
    8       for(String word:wordsInSetence){
    9           if(wordFreqMap.get(word.toLowerCase()) != null){
    10               sentenceScoreMap.put(currentSentence, sentenceScoreMap.get(currentSentence) + wordFreqMap.get(word.toLowerCase()));
    11           }
    12       }
    13       sentenceScoreMap.put(currentSentence, sentenceScoreMap.get(currentSentence)/currentSentence.length());
    14       totalScore += sentenceScoreMap.get(currentSentence);
    15   }
    16
    17   //文のスコアがthresholdの値を満たすと、該当する文はテキストの要約として出力する。
    18   System.out.println("英文テキストの要約結果");
    19   for(Map.Entry<String, Double> kv: sentenceScoreMap.entrySet()){
    20       if(kv.getValue() >= threshold(totalScore)){
    21           System.out.println(kv.getKey());
    22       }
    23   }
    24}
    25
    26//thresholdの値はsentenceScoreの平均値+標準偏差から算出
    27private double threshold(double totalScore){
    28   double mean = totalScore/sentenceScoreMap.size();
    29   double standardDev = 0.0;
    30   for(Map.Entry<String, Double> kv: sentenceScoreMap.entrySet()){
    31       standardDev += Math.pow(kv.getValue() - mean, 2);
    32   }
    33   return mean + Math.sqrt(standardDev/sentenceScoreMap.size());
    34}

    mainメソッド

    そして、プログラムのmainメソッドは、このように書きます。

    1public static void main(String[] args) throws IOException {
    2   SimpleTokenizer simpleTokenizer = SimpleTokenizer.INSTANCE;
    3   TextSummarizer textSummarizer = new TextSummarizer();
    4   textSummarizer.readStopwords();//英文のstopwordsをファイルから読み出す。
    5   //ステップ 1
    6   textSummarizer.detectSentences(inputFile);//入力の英文テキストから文を抽出。
    7   //ステップ 2、3、4
    8   textSummarizer.calculateWordFrequency(simpleTokenizer);//テキストに含まれる単語の頻度を計算
    9   //ステップ 5
    10   textSummarizer.calculateSentenceScore(simpleTokenizer);//各文のスコアを計算し、thresholdの値をみたす文はテキストの要約として出力される。
    11}

    さいごに

    シンプルな方法で、英文の「自動要約プログラム」が完成しました!

    ニュースや資料の長い英文を、自動で要約してくれるので、無駄に感じていた時間を減らせるかもしれません!

    お困りの方は試してみてはいかがでしょうか?

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
    featureImg2020.07.28Java 特集実装編※最新記事順に並べています。Amazon EMRのHadoop完全分散モードクラスタ上でApache Spark...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background