エンジニアMのAI学習記録(2017年11月分)

2017年11月10日
こちらは、Webに関連するエンジニア向けの記事です。
当社のWeb関連技術の公開と採用活動のために掲載しています。

(2017年12月1日更新)

みなさんこんにちは、エンジニアのMです。
これは当社の技術やAIに興味のある方に向けた日誌になります。
今月も毎週更新していきます、よろしくお願いします。

11月6日

■ JavaとPythonの連携をする

KuromojiをPythonから扱えるようにしたい、ということでJavaとPythonの連携について覚える。

  • 参考:(Qiita)PythonからJavaを呼び出す簡単な方法[リンク]

こちらで解説している「py4j」を使ってKuromojiをサーバーとして起動し、Pythonから呼び出して使えるようにする。

■ 手順

Windows環境で作業する。

Anacondaをインストール済みなので、まずはpipでpy4jをインストールする。
Pythonインストール先のshareフォルダ(例:C:\Python36\share)にpy4j0.xx.x.jarができていることを確認。

Eclipseでプロジェクトを作成した後、メニューの プロジェクト>プロパティ>Javaのビルドパス>ライブラリー と辿って「外部JARの追加」から先程の.jarファイルを指定する。
これでサンプルのAdditionApplication.javaを動かして、Pythonからクラスを利用できることができた。

■ 実装

とりあえず簡単にKuromojiを使えれば良いということで、

  • 文を投げると分かち書きして返してくれる
  • 正規表現で記号、数字は勝手に除去する
  • 品詞の組み合わせは「名詞のみ」、「全部」の2通りだけ

と簡素な仕様とする。

package kuromoji4py.io;

import org.atilika.kuromoji.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import py4j.GatewayServer;

public class KuromojiServerIO {

    public String kuromojiIO(String strIn, Boolean onlyNoun) {
        boolean debug = false;
        String strOut = "";
        String word;
        Tokenizer tokenizer = Tokenizer.builder().build();
        Matcher m;
        Pattern p = Pattern.compile("^[0-9]+$"
                + "|^[ -/:-@\\[-\\`\\{-\\~]+$"
                + "|^(+$|^)+$|^%+$|^◯+$|^♪+$"
                + "|^「+$|^」+$|^。+$|^、+$|^.+$"
                + "|^,+$|^■+$|^□+$");

        if(debug) {
            System.out.println("Input:"+strIn);
        }

        try {
            for (Token token : tokenizer.tokenize(strIn)) {
                if(token.getPartOfSpeech().substring(0,2).contentEquals("名詞") || !onlyNoun) {
                    word = token.getSurfaceForm();
                    m = p.matcher(word);
                    if (!m.find()) {
                        strOut = strOut.concat(word+" ");
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Error!");
        }

        if(debug) {
            System.out.println("Output:"+strOut);
        }
        return strOut;
    }
    public static void main(String[] args) {
        KuromojiServerIO app = new KuromojiServerIO();
        GatewayServer server = new GatewayServer(app);
        server.start();
        System.out.println("Kuromoji Server Started");
    }

}

# テスト用Pythonコード(kuromojiIOtest.py)
from py4j.java_gateway import JavaGateway

gateway = JavaGateway()
kuromoji = gateway.entry_point
text = "これはKuromojiのテストです。形態素解析します。記号「」は除去します。"
split = kuromoji.kuromojiIO(text, False)
print(split)
python kuromojiIOtest.py
これ は Kuromoji の テスト です 形態素 解析 し ます 記号 は 除去 し ます

これでPythonから簡単にKuromojiを扱えるようになった。
サーバーとして起動している状態なので、Pythonから呼び出して繰り返し利用することができる。
あとはPython側でファイル入出力のコードを用意して、目的のパスやファイルを処理できるようにすれば良い。

Eclipse上でしか動かせないのは不便なので、仮想CentOSマシンで動かそうとしてみたがなかなかうまくいかず。クラスパスについてまだまだ勉強が足りないようだ。

11月7日

■ Yahoo知恵袋のデータでdoc2vecを試す

昨日はKuromojiを扱いやすくしたので、今日はそれを使って実験を進めてみようと思う。
とりあえず検索ワード「プリンタ」、「Android」について30件ずつ手動で拾ってきて形態素解析した後、doc2vecの結果を見てみる。
一つの質問についてだけ、元の文章を少し変えたものを加えて試してみた。
元々ある程度関連のある文章を並べていて、Kuromojiが名詞と判定した単語を抽出しているので、頻度の高い単語を切る必要はほとんどない。

結果として、ほぼ同じ内容のはずの文章は類似度で上のほうに来るようにはなったものの、突出しているわけではなかった。
今回のような少数の文章に対してはdoc2vec単体ではあまり効果を発揮できないのかもしれない。
他のアイデアと組み合わせることでの可能性を探る必要がありそうだ。
情報が得られそうなワードで検索し、論文や記事から自分の目的に合った手法を見つけることを目標とする。

11月8日

■ Word Mover’s Distance(WMD)を試してみる

  • Word Mover’s Distance: word2vecの文書間距離への応用 [リンク]
  • Earth Mover’s Distance (EMD) [リンク]
  • Word Mover’s Distance を使って文の距離を計算する [リンク]
  • 日本語 Wikipedia エンティティベクトル [リンク]

以上を参考にWMDの実力を試してみた。

今回はJuman++のほうを使った。
wmdistanceを呼び出すときは単語リストを渡す必要があるので、入力された文をその場で分かち書きするのに使っている。

文を入力してください(1つ目)
>> プリンタを選ぶ時に気をつけるべき点はありますか?
文を入力してください(2つ目)
>> プリンタはどこに気をつけて選ぶべきでしょうか?
距離:12.422376563388186

文を入力してください(1つ目)
>> プリンタを選ぶ時に気をつけるべき点はありますか?
文を入力してください(2つ目)
>> Androidの良さについて教えてください
距離:24.573640321058548

文を入力してください(1つ目)
>> プリンタを選ぶ時に気をつけるべき点はありますか?
文を入力してください(2つ目)
>> プリンタを選ぶ時の注意点はなんですか?
距離:12.663622264187333

文を入力してください(1つ目)
>> プリンタを選ぶ時に気をつけるべき点はありますか?
文を入力してください(2つ目)
>> このプリンタでレーベル印刷はできますか?
距離:21.350387439460583

知恵袋のテキストで試した流れで、プリンタとAndroidに関する文で試した結果こうなった。
短い一文で試したが、そこそこ妥当な結果が得られた。

文を入力してください(1つ目)
>> プリンタを選ぶ時に気をつけるべき点はありますか?
文を入力してください(2つ目)
>> 携帯電話を選ぶ時に気をつけるべき点はありますか?
距離:3.1761431100963127

物だけ変えた文の距離がこれくらいだと、あまり信用はできない気もしてくる。
しかし、面倒なパラメータ調整が不要なことは大きなメリットに感じる。
ターミナルでWMDを試すことができるコードを公開する。

# -*- coding: utf-8 -*-

import sys
import re
from gensim import models
from pyknp import Jumanpp

# 学習済みモデルの場所
vectors = "./japanese_entity_vector/entity_vector.model.bin"

# モデル読み込み
print("Wikipedia日本語エンティティベクトルを読み込んでいます")
print("しばらくお待ち下さい…")
model = models.KeyedVectors.load_word2vec_format(vectors, binary=True)
print("{} :読み込み完了".format(vectors))


# 関連語テスト
def similar_words():
    while(True):
        print('')
        word = input("検索したい単語を入力してください\n>> ")
        if word == '':
            break
        try:
            sim = model.most_similar(positive=word, topn=10)
            for x in sim:
                print("{}\t{}".format(x[1], x[0]))
        except KeyError as err:
            print("エラー:単語 {} は辞書にありません".format(word))


# Word Mover's Distanceテスト
def wmd():
     while(True):
         print('')
         temp = input("文を入力してください(1つ目)\n>> ")
         if temp == '':
             break
         replaced=re.sub('\u3000| ', '', temp)
         text1 = split_words(replaced)
         print_words(text1)
         temp = input("文を入力してください(2つ目)\n>> ")
         if temp == '':
             break
         replaced=re.sub('\u3000| ', '', temp)
         text2 = split_words(replaced)
         print_words(text2)
         distance = model.wmdistance(text1, text2)
         print("距離:{}".format(distance))


# Jumanppによる分かち書き
def split_words(text):
    result = Jumanpp().analysis(text)    
    return [mrph.midasi for mrph in result.mrph_list() if check_hinsi(mrph.hinsi)]


# 分かち書き出力条件
def check_hinsi(hinsi):
     if hinsi == "名詞" or\
        hinsi == "未定義語" or\
        hinsi == "動詞" or\
        hinsi == "形容詞":
        return True
     else:
        return False


# 解析結果出力(テスト用)
def print_words(words):
     print("形態素:", end='')
     for word in words:
         print(word+" ", end='')
     print("")


# メイン
print("※何も入力せずにEnterで終了します")
while(True):
    print('')
    word = input("どちらをテストしますか?:similar or wmd\n>> ")
    if word == '':
        break
    elif word == 'similar':
        similar_words()
    elif word == 'wmd':
        wmd()

同じ文を入力してテストしてみたが、特に結果が良くなった感じはしなかった。
何か一つの手法で全部解決すれば良いのだが、そういうわけでもないのが自然言語処理の難しいところか。
文字ベースのCNNによる文書分類の論文があるらしいので、読んでみる。今のマシンパワーでは日本語を扱いきれないと思うが、興味はある。

  • 文字レベルの畳込みニューラルネットワークによる文書分類 [リンク]
  • Character-level Convolutional Networks for Text
    Classification [リンク]

結果を見ると、ユーザーの投稿内容のような、精選されていない文に対する精度が高いことが分かる。
単語ベースのアルゴリズムが、誤字脱字が多く見込まれる文への対応が弱いことはイメージしやすい。
もちろん、あらゆる文字の組み合わせに対応する以上、モデル作成の計算量は膨大なものとなる。
日本語なら文字数は少なくなるが、文字の種類が多いので結果的に英語以上に計算は困難だろう。

日本語に対してもCNNで分類している研究はあるようだ。

  • 文字画像による Character-level Embeddingと文書分類 [リンク]

文字を画像として扱うという方法はなるほどと思った。
漢字は図形であり、似た意味の字は同じ部首を持っていたりするので、次元削減のついでに特徴を抜き出すことができれば確かに都合がいい。

11月9日

■ 文書間距離の計算を別モデルについてもしてみる

Wikipediaエンティティベクトルだけでなく、対象のテキストデータ群から学習したベクトル値も使って文書間距離を確認する。
類似度を計りたいテキストを対象として学習させたデータなので、そのデータ群に対しては汎用的なものよりはフィットしたモデルのはず。
ただし、gensimの学習値は正規化済みだと分かったので、併用するにはそれぞれの距離に対する重みを考えなければならない。

■ 各文書間の距離をリストアップして眺めてみる

各文書間の距離(知恵袋からの30+30文書)を全通りの組合せについて計算して、その数字を眺めて情報を得たいと思う。

  • 文書:Kuromojiで名詞抽出した知恵袋の 2カテゴリ60文書
  • モデル:Wikipediaエンティティベクトルと対象文書から計算したベクトル

この計算の結果、

  • 平均(And+Priは別カテゴリ間の距離の意)
  • Wikiモデル:Android 20.9 / Printer 20.6 / And+Pri 22.4
  • 対象間モデル(x1000):Android 18.2 / Printer 17.2 / And+Pri 21.0

  • 最大

  • Wikiモデル:Android 26.0 / Printer 26.3 / And+Pri 28.0
  • 対象間モデル(x1000):Android 23.2 / Printer 22.2 / And+Pri 23.2

となった。見やすいようにレンジを揃えてある。同一カテゴリ間の距離より、別カテゴリ間の距離のほうが大きいことが分かる。サンプル数が少なくても、確かにある程度分離することができている。
ミックスすることで精度を上げられそうではあるが、2通りだと計算にかかる時間が気にかかった。
ある文書に対して最も距離的に近い文書を見つけ出すような計算には、あまり向かないかもしれない。モデルが大きくなるほど、メモリの消費量も相応に大きくなる。

■ WMD以外の方法についてまた考えてみる

WMDが比較的手軽に文書分類できることが分かったが、運用時にリソースの消費がそこそこ大きそうなので他の方法も検討してみる。

11月10日

■ 類似文を探すということについて

類似文を検索するという行為について、自分の認識にズレがあるのではということに気づいた。
例えば、

  • Androidについて教えてください
  • iOSについて教えてください

という2つの文があったとする。
「何についての話か?」という見方をすれば、AndroidとiOSなので、ジャンル・カテゴリ的には近いが違う物について聞いている。
「これは類似の文か?」という見方をすれば、AndroidとiOSの部分しか違いがないので、これは明らかに類似の文である。
長い文であれば関連するワードの差異によって、類似ではないという判定になるかもしれない。しかしこのような短文だけで見れば、これは間違いなく類似の文である。

認識のズレというのはまさにこのことである。何についての文か、というところと、文法的に類似の文か、というところで違うのに、全部まとめて「類似文」として処理させようとしていた。
比較的短い文に対して何についての文か知りたいのであれば、単純に類似文検索の手法を持ち込むのは適切ではなかった。

勉強としては良かったが、もう少し目的と手法について検討しながら進めるべきだったと思う。

■ 改めて手法について考える

何を持って類似性があるとみなすか、というところが重要なポイントとなる。
例えば、「家電Aは値段の割に高性能でオススメ。例を挙げると(以下略)」のような文があれば、

  • カテゴリ:家電・電化製品
  • 感情:好意的
  • 文体:カジュアル
  • 文の形式:商品レビュー
  • テーマ:家電Aについて

のようにタグ付けできるとする。
家電Aについてのレビューが欲しければ、感情・文体は関係ないのでカテゴリ・文の形式・テーマについて分類して、完全一致したものを抽出すれば良い。
テーマについては未知語が該当するケースも多いと思われるので、方法について検討する必要があるかもしれない。

例えば教師データとしてタグ付けを済ませた文章から、Doc2Vecで文章のベクトル値を得る。
そのベクトル値についてディープラーニングによる教師あり学習によって、適切にそれぞれ分類できるよう学習させる。
分類精度は十分に上げられそうだが、この方法は人力で教師データを用意しなければならないのが悩ましい。さほど多くないデータ量で精度を上げるには悪くない方法だとは思うのだが。

■ この方法について

実際に運用するとしたら、新規文書のベクトル値を求めた後に行列演算でタグ付けして保存するだけなので、負荷は小さく済みそうだ。大きな辞書データを保持する必要があるので、メモリ消費量はそれなりか。
人手で枠組み(タグの数、種類)を決める部分も検討を重ねる必要がある。例えば感情をプラス・マイナスの2通りから喜怒哀楽の4通りにしたい、となった場合、一から学習させなければならないからだ。


11月13日

■ テーマ分析について

先日の手法で一番難しいのが、文のテーマ(何について書いている文か)を推定することである。
テーマとして抽出されるのは名詞しかありえないが、その名詞をどうやって抽出するか。パターンマッチングなら「XXは」、「XXが」などの場合のXXは文のテーマである可能性は高い。
しかし、パターンマッチングだけでは柔軟性に欠ける。
参考になりそうな先行研究を探して、応用できないか考えることにする。

■ 主題推定は難しそう

読んだのは古めの論文が主だが、分析の仕方を工夫して精度を上げようとしているにも関わらず、実用上はアテにできそうにない精度であった。
それだけ自然言語を機械に理解させるのは難しいということなのだろう。
教師あり学習で分類できるだけ分類し、候補を絞り込んだ後に、コサイン類似度やWMDを用いてテーマが近そうなものを選択する、といった方法でもいいかもしれない。
自分で実験済みの手法で解決できる可能性があるなら、試してみる価値はありそうだ。そもそも、DNNによる分類がうまくいくかどうかもまだ分からない。

11月14日

■ Word2Vec

以前の案では最初の処理としてWord2Vecを使っているので、それ以降の処理についてもWord2Vecが持つ弱点(多義語、対義語に弱いなど)の影響は続く。
念頭に置いておく必要がある。

■ 半教師あり学習

ラベル付けされたデータと、ラベル付けされていないデータの両方を使って学習させるSemi-Supervised Learningのこと。準教師あり学習と呼ばれたりもする。
データ量が多いと人手で全部にラベルを用意することはほぼ不可能なので、この手法が利用できるなら利用したい。調べてみようと思う。

  • Semi-Supervised Learning with Ladder Networks [リンク]
  • Semi-Supervised Learning [リンク]

■ Microsoft Azureの話題になったので調べてみる

Microsoft Azureのお世話になるかもしれないということで、Azure MLのチュートリアルなどを読みながら、サービス内容について調べてみる。

11月15日

■ 半教師あり学習続き

日本語でも理解できるかどうかの内容を、英語で理解するのは非常に難しい。
論文によると、MNISTでは劇的な効果が得られていたが、CIFAR-10ではそれほど効果が見られなかったという。
問題が複雑になるほど、工夫を重ねても少ないラベル数で学習するのは難しいと考えて良さそうだ。

■ 調べ物続きなので手を動かしたい

Doc2Vec+教師あり学習の有効性を確認するために、知恵袋データ(例の60件)に対してDNNによる分類を試してみる。
インタプリタで、

from gensim import models
model = models.Doc2Vec.load("chiebukuro.bin") # 学習済み知恵袋モデル
for i in model.docvecs: # 300次元ベクトルの取得確認
    print(i)
for i in range(len(model.docvecs)): # idと対応するtxtファイルの並び確認
    print(model.docvecs.index_to_doctag(i))

として、必要な情報を得られることを確認。
さすがにデータが足りないと思われるので、100件になるまで補充する。
その作業の過程で、Androidにも関係があり、プリンタにも関係がある質問を見つけた。こういう場合に排他で処理してしまうと、どちらか一方でしか検索できないことに気がついた。
1つの文章の中に、質問と要望、ここが良くてここが悪い、といった違う内容の文が入るケースを想定すると、こちらで完全に切り分けてしまうのは都合が悪いようだ。

■ タグで処理する場合

タグが1,000種類くらいあって、テキストが100,000個くらいあっても、1個のテキストが持つタグの数は多くて10個程度だろう(うまく分類できていれば)。
つまりほとんどの要素が0の行列なので、scipy.sparse等のライブラリを使って「疎行列」として扱ってやれば高速に処理できる。
人力で教師データのためにタグ付けするのが厳しい作業だが、機械学習で法則を数十通りも見つけ出してもらうよりは早く終わるだろう。

11月16日

■ WMDを確認

データ数を100件に増やし、出現回数が極端に少ない単語のみ除外した知恵袋データに対しても、WMDを確認する。
同一カテゴリ間での距離のほうが少し近いという結果だった(平均で5~7%くらい)。
今回は以前より単語数も多く、100×100件分のWMDを計算させると10分以上かかっていたので、やはり大規模な処理には向いていない。

■ Gensim

WindowsからpipでインストールしたGensimがうまく動かない。
pip経由ではダメなのだろうか。
仕方ないので、docvecsだけ抜き出してpickle化して使うことにする。

■ ニューラルネットワークによる分類

データ分割をして、学習させることはできた。しかしあまりにもデータ数が不足しているので、学習用データにすぐオーバーフィットしてしまう。
Doc2Vecの時点でかなりの情報量が削減されている上に、うまく機能しているかどうかの判断も難しい。

11月17日

■ 文章の特徴抽出

ディープラーニングは特徴抽出が肝なので、他に使える方法が無いか考えてみる。Word2Vec(Doc2Vec)は情報をかなりシンプルにしている分、ディープラーニング向きでは無さそうだ。

■ TensorFlowのVer1.4がリリースされたので更新する

一部コードの互換性が無くなるが、しっかり更新しておく。


11月21日

■ 類似文検索のタスクはDoc2Vecで

少し間が空いたので、再検討してみた。
文の類似度に関しては、素直にDoc2Vecで文をベクトル化して検索したほうが良いという結論に至った。別の分類タスクを用意する場合には、他の方法について検討する必要があるだろう。

11月22日

■ チャットボットを作ってみる

  • seq2seqのチャットボットを動かしてみた話 [リンク]

こちらを参考にチャットボットを作ってみる。
類似文検索などに関するアイデアが行き詰まり気味なので、少し違うことをやって頭をリフレッシュしたいと思う。

■ MeCabのインストール

定番中の定番の形態素解析器である「MeCab」を今までインストールしてこなかった。
コードを借りてきて動かすためにインストールしようとしたのだが、一部詰まってしまったのでうまくいった手順を示す。

  • Python3で形態素解析エンジンMeCabを使えるようにする(2016年3月版) [リンク]
  • PythonからMeCabを使ってみる。 [リンク]
git clone https://github.com/taku910/mecab.git
cd mecab/mecab
./configure  --enable-utf8-only
make
make check
sudo make install

cd ../mecab-ipadic
./configure --with-charset=utf8
make
sudo make install

pip install mecab-python3

ここまでの段階でPythonインタプリタからimportを試みたところ、

ImportError: libmecab.so.2: cannot open shared object file: No such file or directory

と表示されて読み込みに失敗した。
そこで以下の.confファイルを作成する。

vi /etc/ld.so.conf.d/(任意のファイル名).conf

として、

/usr/local/lib

とだけ記述して保存する。参考ページの方法で直接ld.so.confにパスを書き込んでも動作したが、他の構成に倣って.confファイルを追加した。
これで問題なくMeCabをPythonからimportして使うことができるようになった。
ようやく本題に入ることができる。

■ とりあえずTwitterから対話を取得させる

Twitter API(およびtweepy)に関する知識は一切ないので、各コードの作者に感謝しつつ実行する。
今後も対話データを使う可能性を考えて多めに取得しておく。
3時間程度放置した後で対話データの容量を確認したら、約1.7MBだった。
仮に17MB分確保しようと思ったら、あと27時間程かかる計算だ。
とりあえず今日一日はこれを回し続けることにする。

■ Differentiable Neural Computers(DNC)

待ち時間を利用して調べ物をしていたら、外部記憶を持つニューラルネットワーク、DNCに関する記事を見つけた。Neural Turing Machine(NTM)の後継にあたるものらしい。
RNNやLSTMよりもさらに記憶力の向上したニューラルネットワーク、と解釈した。
チャットボットに応用したら、過去のやり取りに基づいた会話がより高い精度でできそうだ。

■ ソースコードを見る

自分でコードをいじってみる時のためにソースコードを読んでみる。
しかし、軽くコードの流れを追っただけではとても理解できそうになかった。
まだまだ対話データの取得には時間がかかるので、その間にコードを理解できるよう頑張ってみたほうが良さそうだ。

11月24日

■ WindowsにMeCabをインストールする

色々調べて試してみたが、

  • Windows で pip で mecab-python をいれる [リンク]

こちらのコンパイル済み「MeCab(64bit)」と「mecab-python-windows」をインストールするのが簡単だった。
「Windows」の「Python」上で「MeCab」を使う、というピンポイントな需要にたまたま応えてくれていた作者の方に感謝したい。
必要なものが揃っていないとPythonラッパーが入らないので、情報を参考に、

のインストールと、

C:\Program Files\MeCab\sdk を、
C:\Program Files (x86)\MeCab\sdk へコピーしたらうまくいきました。

というコメント欄の情報に従ってmecab.hをコピーして、インストール作業を進める。
全て終わったら再起動をして作業完了。
site-packageのデータをコピーして、PyCharmからもimportできることを確認した(AnacondaのPython 3.6.2 で確認)。
これでチャットボットの学習作業をWindows側で実行できるはず。

■ 「Amazon Echo」について調べてみた

話題の「Amazon Echo」を予約しているということなので、スキルの作り方について調べてみる。
公式でも丁寧に解説されているので、至極単純なものであればそれほど苦労せずに動かすことができそうだ。

■ ここまでで取得できた対話データを使って学習させてみる

train.pyと同じ階層に、「data」フォルダと「generated」フォルダを作成し、data_processor.pyを実行して辞書を作成する。
フォルダをあらかじめ作成しておかないと、エラーが出て止まってしまう。
data_processor.pyをWindows上のPycharmから実行したところ、いつもの文字コードエラーが出た。そのため、data_processor.pyは仮想CentOS側で実行した。
次にtrain.pyをPycharmから実行すると、パスが見つからないと怒られてしまう。
これは、

checkpoint_path = "seq2seq.ckpt" # value error
checkpoint_path = "./seq2seq.ckpt" # OK!

こちらのパス指定が悪かったらしい。Pycharmから実行するときは気をつける必要がある。
借りてきたコードで学習させるだけなのに随分とバタバタしてしまった。
8MB分の対話データで実験したところ、GTX 1050Tiを使って、500stepあたり4分かかった。


11月27日

■ MeCab+NEologdを試してみる

短めの文章に自動でタグ付けする方法として、NEologdの解析結果を利用するのはどうだろうかと思ったので試してみた。
ipadicとNEologdでの解析結果を照らし合わせて、

  • 各辞書で解析結果が食い違う名詞、固有名詞
  • 解析結果が一致する名詞、固有名詞

があった時には、前者のほうが重要と考える。またこの場合は、出現頻度が低い名詞のほうが重要な可能性が高い。
機械、部品といった一般的な単語と、製品の型番や製品名のような単語では、前者のほうが結果は一致しやすく、出現頻度が高い。
一方後者は辞書に無い単語である可能性が高いので、解析結果は一致しづらく、また出現頻度も低い。
例えば同じアイテムに関する文を検出するだけなら、形態素解析が必ずしも正確である必要は無く、間違った分割の一致を取るだけで問題ない。
この方法最大の欠点は、誤字の含まれる文(特に固有名詞の誤字)を抽出できない点である。あくまでも短い文に対する、簡易的な文抽出手段としての提案となる。
次は具体的な実装方法について考えていく。

11月28日

■ 実装中

  • 簡単化のために辞書の使い分けは無し(NEologdのみ)
  • 誤字への対応として、レーベンシュタイン距離を使って単語の(文字)類似性を計算に組み込む

計算コストが掛かると思い、レーベンシュタイン距離については特に言及していなかったが、それほどの計算量にならなさそうなので入れることにした。
程々にライブラリを活用しつつ、自分で実装できるところは実装する。

■ 進捗

  • ドキュメントから単語リストの作成
  • 単語辞書の作成
  • 単語の出現度数表(固有名詞:小、一般名詞:大)の作成

後々のために、単語辞書などに関わる主要な部分はクラス化した。
どこまでこちらのイメージ通りに機能してくれるだろうか。
ある程度融通が利くように書いているつもりなので、精度がイマイチであればtf-idfを試したりするといったこともできるはず。

11月29日

■ 進捗

  • 単語同士のレーベンシュタイン距離と出現度数表から算出した、単語組の評価値行列を作成
  • 評価値の合計に基づき、文の類似度を計算(文の長さに差があれば補正する)

やっていることがWMDに近いせいか非常に計算が遅い。
類似度の算出自体は大体期待通り。小規模で短めの文という制限はつくが、精度自体は悪くないと思う。

11月30日

■ 進捗

  • データのセーブ・ロード
  • 計算式の調整、非効率な計算の改善

for文やif文を駆使して実装している都合上どうしても処理が遅い。
ループの組合せを工夫したり、無駄な処理が発生しないようにcontinueを積極的に使うなどして少しづつ効率化させていく。
結果昨日のバージョンと比較して、辞書作成は2倍以上、文の類似度算出は4倍以上高速化できた(元が遅すぎただけでもある)。
疎行列のおかげでメモリは節約できている。

12月1日

■ 進捗

  • レーベンシュタイン距離を使わない処理の実装(評価値行列、類似度計算)

レーベンシュタイン距離を使わない場合、実質的に文章内の名詞で検索をかけているのとほぼ変わらない。
最低限の機能は揃って、一区切り付いたように思う。
Gensimなどのライブラリを思い浮かべながら、クラス構造を考えつつ実装したのはいい勉強になった。

■ 情報収集

AIや人工知能についての記事がかなり増えている印象なので、調べながら参考になりそうなものを探す。

■ 学習を回していたチャットボットを試してみる

約35MB分の対話データを使って学習させた。
5時間以上回してStep数39000まで進んだので、この時点で試してみた。

> おはよう
お は あり ー !
> 気分はどう?
いい よ よ ! !
> 今日は何してた?
寝 た ! ! ! ! ! ! ! !
> 他には?
いや … …

Twitterから取得しただけあって、やり取りもそれっぽい。ずっと寝てたらしい。
ただ、UNK(未知語?)が返ってくることが多く、あまりいいやり取りはできず。
せっかくデータが集まったわけなので、手直しできそうであれば直してみてもいいかもしれない。


フォームズ編集部
オフィスで働く方、ネットショップやホームページを運営されている皆様へ、ネットを使った仕事の効率化、Webマーケティングなど役立つ情報をお送りしています。