0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java初心者】Wekaによる機械学習 07-自然言語処理を使ったテキストの現実的な分類

Posted at

今回も、試行錯誤の結果を自分用にメモしたものです。
間違っているところがありましたら、ご指摘ください。

目的

やりたいことは

  • 短いテキストデータを教師データとし、行ごとのカテゴリーをつけておく
  • 上記データでモデルを作って保存
  • 分類が必要なデータ(本番データ)は別にあり、上記モデルを利用する

用意するアプリ

用意するデータ

  • 教師データ。いろいろなニュース記事の中から、ロサンゼルス近郊の火災の記事と、国内の記事を一文ずつあつめて形態素解析で分かち書きにしておく。(自分は IgoというJavaのライブラリーと、ipadoc + Neologdを使いました)カンマで区切って「国際」または「国内」というカテゴリーをつける。train.csvとして保存。
  • ストップワード。(ただし、上記教師データに即したものです。本当はきちんと精査するべきですがか、説明用として作りました)stop.txtとして保存。
  • 本番データ(テストデータ)。教師データと似たような形式のCSV。(説明用にテストが成功しやすいものにしています)

カレントディレクトリに data というディレクトリを作り、その下に csv , arff , model , dic という4つのサブディレクトリをつくりました。csvデータは csvに。ストップワードは dic に入れます。もちろんコードの書き方に応じて変更可能です。

教師データ(data/csv/train.csv)の例

原稿,カテゴリー
ドジャース  本拠地  置く ロサンゼルス  非常事態  見舞わ   いる  ,国際
特に ロサンゼルス 西部   パリ セーズ 火災   北部 近郊  アルタデナ 周辺  発生    イートン 火災      110 平方キロ  焼け  鎮圧  メド  立っ   ない  ,国際
すでに 死者  5   おり  バイデン 大統領  この   過去最悪  山火事    述べ   ,国際
 カリフォルニア州 ロサンゼルス 西部  大規模  山火事  続い  いる  ,国際
9 まで  5  死亡  確認     当初  7  発表        ロサンゼルス郡  ロバート  ルナ 保安官   イートン 地区   死者  下方 修正    ため  実際   5   説明    ,国際
英国  ヘンリー王子 ( 40 )  メーガン妃 ( 43 ) 夫妻  9   カリフォルニア州 ロサンゼルス  襲っ  壊滅   山火事  被災   人々   支援  訴え   ,国際
 南カリフォルニア  山火事   題する 声明  夫妻  公式ホームページ  掲載  ,国際
 ここ    南カリフォルニア  山火事  近隣 地域  襲い  家族  学校  医療センター  その他 多く  もの  破壊   あらゆる 階層      影響  与え   ます   つづり  温かい 食事  提供 する 慈善団体  被災   動物  世話 する 団体 など 現地  支援 活動  する 団体  リスト  共有    ,国際
 西部 カリフォルニア州 ロサンゼルス 近郊  複数 地域  発生   山火事   地元当局  9  損壊   家屋 など  建物   9000  以上   られる  発表    ,国際
   発生   火災   強風  干ばつ により 消火活動  難航  9   新た  山火事  避難命令    おり  予断  許さ ない 状況  続く  ,国際
バイデン 大統領  9  ホワイトハウス  会議  招集    カリフォルニア 史上  最も 広範囲 にわたって 甚大  被害  もたらし  いる 火災    指摘  ,国際
埼玉県川口市   市内   み処理 施設  起き  火災  影響   9  10  2日間  一般 ごみ  収集  停止  まし   ,国内
  近隣  自治体 など  協力  依頼    週明け  今月13 から 収集  再開 する  発表  まし   ,国内
今月3  市内   み処理 施設  朝日環境センター   火災  あり  電気 系統  ごみ  運ぶ クレーン など  被害  受け まし   ,国内
川口市  市内  ある もう 1   み処理 施設  対応    まし    処理  追いつか なく なっ  こと から  9  10  2日間  燃える ごみ など  一般 ごみ  収集  停止  まし   ,国内
11 午前0 50 ごろ  神奈川県横須賀市田浦泉町  秋山 ハナ さん ( 93 )      火事  なっ  いる    ( 64 ) から 通報  あっ   ,国内
県警  よる   住宅  全焼   焼け跡 から 2  遺体  見つかっ   ,国内
秋山 さん   同居 する   50   連絡  取れ  おら   身元  確認  進める  ,国内
きのう   岐阜県  三重県  住宅 火災  相次い  発生   1  遺体  見つかり まし   ,国内
消防  警察  より ます  きのう 午後9  ごろ  羽島市 正木町  住宅    から     いる   近く  住む  から 消防  通報  あり まし   ,国内
消防車 6   出て   およそ 3時間後  消し止め られ まし    この 火事  木造2階建て  建物  焼け  焼け跡 から 性別不明  1  遺体  見つかり まし   ,国内
この   住む 男性  連絡  取れ  おら   警察  身元  特定  進め   ます  ,国内
また  この 直後   尾鷲市南陽町  住宅   火事  あり  消防車 12   出て   およそ 3時間後  消し止め られ まし   ,国内
10 午後2 ごろ  群馬県神流町生利  建物 火災  発生    近く  山林  延焼     られる  ,国内
多野 藤岡 広域 消防  よる   午後4 現在  搬送    ない  ,国内
 消防   防災 ヘリ による 消火活動  続い  いる  ,国内
10 午前  宇部市  民家 3   全焼 する 火事  あり まし   ,国内
火事  あっ     宇部市西岐波  民家 です  消防  より ます  10 午前8 20 ごろ 近隣  住民 から  爆発音  聞こえる   通報  あり まし   ,国内
9 午後6 50   大阪市旭区赤川  木造2階建て 住宅 から 出火  ,国内
近く  建物   延焼       焼き  住宅  焼け跡 から 1  遺体  見つかっ   ,国内
住宅  住人  連絡  取れ  おら   大阪府警    遺体  身元確認  進め  いる  ,国内
11 未明  神奈川県横須賀市  住宅  火事  あり  この   住む 2  連絡  取れ なく なっ   ます  ,国内
11 午前1 ごろ  横須賀市田浦泉町     燃え  いる   消防  通報  あり まし   ,国内
10 午後  観音寺市  住宅  火事  あり  男性 1  意識不明  状態  病院  運ば  まし   その後  死亡  まし   ,国内
10 午後3 すぎ  観音寺市 大野原町  住宅 から     いる   近く  住む  から 消防  通報  あり まし   ,国内
愛知県警 など  よる   11 午前11 10 ごろ  同県 愛西市  木造2階建て 住宅  燃え  いる  119  あっ   ,国内
1  救助    病院  搬送     この   住む 4  うち 3  連絡  取れ   ない  いう  ,国内
10    北谷町  住宅 1   全焼 する 火事  あり まし   この 火事 による けが人   ませ  でし   ,国内
警察  消防  より ます   火事  あっ    北谷町桑江  住宅街   10 午後9   2 から     いる   近く  住む  から 警察  通報  あり まし   ,国内
県警 など  よる   昨年12 30  熊本市西区花園 7 丁目  住宅 火災   1 暮らし  男性 ( 73 )  焼死    ,国内
110 午前  時半 過ぎ  宇都宮市駒生町  住宅 から     いる  近く  住む  から 消防  通報  あり まし   ,国内
豊川市消防本部   昨年 管内  発生   火災  38 だっ   発表    ,国内

本番データ(テストデータ・data/csv/test.csv)の例

原稿,カテゴリー
2   東京・清瀬市  団地の 1   火事  なり  1  病院  搬送   まし   その後  確認   まし   ,?
アメリカ 西部 ロサンゼルス 近郊  山火事   警察  行方不明者  捜索 にあたる 特別 チーム  つくり  活動  本格      ます  ,?
アメリカ 西部 ロサンゼルス 近郊  パシフィック・パリセーズ  7  発生   山火事   非常  強い   影響  急速  燃え広がり  広い 範囲  延焼  続い   ます ,?
ロサンゼルス 周辺  相次い  いる 山火事   スポーツ界   NBA  アメリカ プロバスケットボール   NHL  北米 プロアイスホッケーリーグ  一部  試合  延期  なる など  影響  広がっ   ます ,?
11 未明  神戸市  集合住宅  火事  あり  この 建物  4  住む 男女 2  病院  搬送   意識不明  重体  なっ   ます  ,?
11 午前2 10 ごろ  神戸市兵庫区湊町  4階建て  集合住宅       いる   近く     など から 110番通報  あり まし   ,?

ストップワード(data/dic/stop.txt)の例

1
10分
10日
110
119番
11日
12
1つ
1人
1月10日
20分
2人
2日間
2階
3
30日
38件
3人
3時間後
40
43
4人
50代
50分
5人
6
64
7
73
7人
9000
93
9日
、
。
「
」
あっ
あらゆる
あり
ある
い
いう
いる
うち
およそ
おら
おり
から
が
きのう
けが人
ここ
こと
この
ご
ごみ
ごろ
さ
さん
し
すぎ
すでに
する
ず
その他
その後
た
ため
だ
だっ
つづり
て
で
でし
です
と
な
ない
なく
なっ
など
に
により
による
にわたって
の
は
へ
まし
ます
ませ
また
まで
み
み処理
も
もう
もたらし
もの
や
より
よる
られ
られる
れ
を
ん
午前
午前0時
午前11時
午前1時
午前8時
午後
午後2時
午後3時
午後4時
午後6時
午後9時
5
6
7
・
丁目
万
今月13日
今月3日

Javaで書いてみました

教師データの読み込みからモデル作成まで

package qiitatest;

import java.io.File;
import java.io.IOException;
import weka.classifiers.bayes.NaiveBayes;
import weka.core.Instances;
import weka.core.SerializationHelper;
import weka.core.converters.CSVLoader;
import weka.core.stopwords.WordsFromFile;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.NominalToString;
import weka.filters.unsupervised.attribute.StringToWordVector;
import java.nio.file.Files;
import java.nio.file.Path;

public class TrainClass {

    public static void main(String[] args) throws IOException, Exception {
        // CSVをArffに
        CSVLoader csvLoader = new CSVLoader();
        csvLoader.setFile(new File("data/csv/train.csv"));
        Instances csvIns = csvLoader.getDataSet();
        Files.write(Path.of("data/arff/train.arff"), csvIns.toString().getBytes());
        // テキストのAttrを設定する。この場合は冒頭(1番目)
        NominalToString n2s1 = new NominalToString();
        n2s1.setAttributeIndexes("1"); // setInputFormatより先に設定すること
        n2s1.setInputFormat(csvIns);
        Instances train_string_instances = Filter.useFilter(csvIns, n2s1);
        Files.write(Path.of("data/arff/train_string.arff"), train_string_instances.toString().getBytes());
        // 単語ベクトルにする。さらにストップワードも定義
        StringToWordVector s2wv = new StringToWordVector();
        s2wv.setInputFormat(train_string_instances);
        WordsFromFile stopwords = new WordsFromFile();
        stopwords.setStopwords(new File("data/dic/stop.txt"));
        s2wv.setStopwordsHandler(stopwords);
        s2wv.setDictionaryFileToSaveTo(new File("data/dic/arff.txt"));
        Instances train_string_wv_instances = Filter.useFilter(train_string_instances, s2wv);
        Files.write(Path.of("data/arff/train_string_wv.arff"), train_string_wv_instances.toString().getBytes());
        // モデル作成
        NaiveBayes nb = new NaiveBayes();
        train_string_wv_instances.setClassIndex(0);
        nb.buildClassifier(train_string_wv_instances);
        SerializationHelper.write("data/model/fire.model", nb);
        System.out.println(nb.toString());
    }
}

上記コードで、data/arff の中に、CSVから変換したarffが3つ保存されます。
いちいち保存する必要はないのですが、内容を見ると、処理の内容が確認できるはずです。

また、モデルは data/model の中に fire.model という名前で保存されます。
今回は単純ベイズ法を使いましたが(NaiveBayes)、他のアルゴリズムも使用できます。

実際の分類(本番・テスト)

package qiitatest;

import java.io.File;
import java.io.IOException;
import weka.classifiers.Classifier;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.SerializationHelper;
import weka.core.converters.CSVLoader;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.FixedDictionaryStringToWordVector;
import weka.filters.unsupervised.attribute.NominalToString;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import weka.core.converters.ArffLoader;

public class TestClass {

    public static void main(String[] args) throws IOException, Exception {
        // テストデータをCSVから作る
        CSVLoader csvLoader = new CSVLoader();
        csvLoader.setFile(new File("data/csv/test.csv"));
        // 検証用にテストデータを行ごとのリストにしておく
        List<String> readLines = Files.readAllLines(Path.of("data/csv/test.csv"));
        Instances testCsvIns = csvLoader.getDataSet();
        Files.write(Path.of("data/arff/test_csv.arff"), testCsvIns.toString().getBytes());
        // 「カテゴリー」が ? なので、モデル作成時に作ったARFFからフィールド情報をもらう
        ArffLoader af = new ArffLoader();
        af.setFile(new File("data/arff/train_string.arff"));
        Instances trainStringArff = af.getDataSet();
        trainStringArff.clear(); // してもしなくてもいい
        // ここからフィールド1をStringにする
        NominalToString n2s2 = new NominalToString();
        n2s2.setAttributeIndexes("1"); // setInputFormatより先に設定すること
        n2s2.setInputFormat(trainStringArff); // カテゴリーのAttrを整えてくれる
        Instances testFilter = Filter.useFilter(testCsvIns, n2s2);
        Files.write(Path.of("data/arff/test_csv_string.arff"), testFilter.toString().getBytes());
        // テストデータを単語ベクトルにする
        // モデル作成時に作ったAttr情報と揃える必要がある
        FixedDictionaryStringToWordVector fixedDirSwInst = new FixedDictionaryStringToWordVector();
        fixedDirSwInst.setDictionaryFile(new File("data/dic/arff.txt"));
        fixedDirSwInst.setInputFormat(testFilter);
        fixedDirSwInst.setTFTransform(true);  // TF を使用
        fixedDirSwInst.setIDFTransform(true); // IDF を使用
        Instances testS2vIns = Filter.useFilter(testFilter, fixedDirSwInst);
        Files.write(Path.of("data/arff/test_csv_string_wv.arff"), testFilter.toString().getBytes());
        // モデルを使用して1行ごとに判別する
        Classifier fc = (Classifier) SerializationHelper.read("data/model/fire.model");
        testS2vIns.setClassIndex(0);
        int n = 0;
        for (Instance instance : testS2vIns) {
            double classifyInstance = fc.classifyInstance(instance);
            String value = testS2vIns.classAttribute().value((int) classifyInstance);
            System.out.print(value);
            System.out.println(":" + " -> " + readLines.get(n + 1));
            n++;
        }
    }
}

分類結果

以下のようになりました。まぁ、すべての原稿がこのように分類できるわけではないと思いますが、仕組みの説明としてご覧ください。

国内: -> 2日 朝 、 東京・清瀬市 の 団地の 1 室 が 火事 に なり 、 1人 が 病院 に 搬送 さ れ まし た が その後 が 確認 さ れ まし た 。 ,?
国際: -> アメリカ 西部 ロサンゼルス 近郊 の 山火事 で 、 警察 は 行方不明者 の 捜索 にあたる 特別 チーム を つくり 、 活動 を 本格 化 さ せ て い ます 。 ,?
国際: -> アメリカ 西部 ロサンゼルス 近郊 の パシフィック・パリセーズ で 7日 に 発生 し た 山火事 は 、 非常 に 強い 風 の 影響 で 急速 に 燃え広がり 、 広い 範囲 で 延焼 が 続い て い ます 。,?
国際: -> ロサンゼルス 周辺 で 相次い で いる 山火事 で 、 スポーツ界 で も NBA = アメリカ プロバスケットボール や 、 NHL = 北米 プロアイスホッケーリーグ の 一部 の 試合 が 延期 と なる など 、 影響 が 広がっ て い ます ,?
国内: -> 11日 未明 、 神戸市 の 集合住宅 で 火事 が あり 、 この 建物 の 4階 に 住む 男女 2人 が 病院 に 搬送 さ れ 意識不明 の 重体 と なっ て い ます 。 ,?
国内: -> 11日 午前2時 10分 ごろ 、 神戸市兵庫区湊町 の 4階建て の 集合住宅 で 「 火 が 出 て いる 」 と 近く に い た 人 など から 110番通報 が あり まし た 。 ,?
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?