Javaで簡単な英文自動要約(Automatic English Text Summarization)プログラムを作る
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 のインストールと使い方は、過去の記事などを参照にしていただければ!
関連記事
関連サイト
【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つの文が得られます。
- 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.
文の抽出をするメソッドのコード
文を抽出するメソッドのコードは、下記のように書きます。
メソッドに「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」 と言います。
トークン化の例を使うと、重み付けの単語を表にまとめると、こんな感じになります。
単語 | 頻度 |
Claire | 1 |
left | 1 |
New | 1 |
York | 1 |
hoping | 1 |
have | 1 |
had | 1 |
time | 1 |
explore | 1 |
sites | 1 |
she | 1 |
wait | 1 |
return | 1 |
city | 1 |
future | 1 |
コード
ステップ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」は、スコアの平均値+標準偏差から算出されます。
文のスコア
上述の例を使うと、文のスコアはこのような感じになります。
単語 | 頻度 |
Claire | 1 |
left | 1 |
New | 1 |
York | 1 |
hoping | 1 |
have | 1 |
had | 1 |
time | 1 |
explore | 1 |
sites | 1 |
she | 1 |
wait | 1 |
return | 1 |
city | 1 |
future | 1 |
文のスコア=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}
さいごに
シンプルな方法で、英文の「自動要約プログラム」が完成しました!
ニュースや資料の長い英文を、自動で要約してくれるので、無駄に感じていた時間を減らせるかもしれません!
お困りの方は試してみてはいかがでしょうか?
こちらの記事もオススメ!
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
2020.07.28Java 特集実装編※最新記事順に並べています。Amazon EMRのHadoop完全分散モードクラスタ上でApache Spark...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit