GuidedLDAで特許課題解決手段マップ作成(前半)


1.目的

 特許文章は長いので効率的に読みたい、または特許群として全体的な傾向を把握したい。この際に文章を「課題(目的)」と「解決手段」でカテゴリ分けしてマップ化できると把握がしやすい。図としては下記のような感じ。


参照:http://www.sato-pat.co.jp/contents/service/examination/facture.html

 この課題軸と解決手段の軸(ラベル)を文章から自動的に抽出したい。問題意識はこの記事とほぼ同じ。やり方の1つにLDAがある。しかし、通常のLDAではトピックが自由に操作できない。人間が「このトピックにはこんな言葉が出る(出てほしい)」というのを調整できる方法として、ガイド付きLDA(GuidedLDA)がある。これを使ってほしいように軸をうまく設定できるか見る。

2.ガイド付きLDA

ガイド付きLDAの概要は こちらこちら参照。
公式

3.処理の流れ

まずは出力が出来るとこまでもっていく。

1.必要なライブラリや関数定義

#@title ←【STEP1】(<font color="red">必須</font>)実行準備
!pip install guidedlda
import numpy as np
import pandas as pd
import guidedlda

#コーパスからonehotエンコード結果を作成するのための関数()
from sklearn.feature_extraction.text import CountVectorizer
def get_X_vocab(corpus):
    vectorizer = CountVectorizer(token_pattern='(?u)\\b\\w\\w+\\b')
    X = vectorizer.fit_transform(corpus)
    return X.toarray(), vectorizer.get_feature_names()

#トピック毎に主要語を抽出する関数
def out1(model,vocab):
    n_top_words = 10
    dic = {}
    topic_word = model.topic_word_
    for i,topic_dist in enumerate(topic_word):
        topic_words = np.array(vocab)[np.argsort(topic_dist)][:-(n_top_words+1):-1]
        print('Topic {}: {}'.format(i, ' '.join(topic_words)))
        dic['topic'+str(i)] = ' '.join(topic_words)
    return dic 

2.特許データ(csvとかexcel)をpandas dataframeに格納。

mecabで分かち書き(mecabじゃなくてもいいと思う)。colは処理対象の列。

df[col+'_1g']= df[col].apply(wakati,args=('DE',))

3.処理対象としたい列の指定(課題軸と解決手段軸の2つ)

#@title ←【STEP2】(<font color="red">必須</font>)処理対象の列指定
col_name = "発明が解決しようとする課題_1g" #@param {type:"string"}
col_name2 = "請求の範囲_1g" #@param {type:"string"}

df[col_name].replace({'\d+':''},regex=True,inplace=True)
df[col_name2].replace({'\d+':''},regex=True,inplace=True)

#コーパス⇒X(文書単語頻度行列&vocabリスト出力
corpus = df[col_name].apply(lambda x:" ".join(x.split("|")))
X,vocab = get_X_vocab(corpus)
word2id = dict((v,idx) for idx,v in enumerate(vocab))

#コーパス⇒X(文書単語頻度行列&vocabリスト出力
corpus2 = df[col_name2].apply(lambda x:" ".join(x.split("|")))
X2,vocab2 = get_X_vocab(corpus2)
word2id2 = dict((v,idx) for idx,v in enumerate(vocab2))

print("抽出された語彙リスト---------------")
print(vocab)
print("単語数:"+str(len(vocab)))
pd.DataFrame(vocab).to_csv(col_name+"単語リスト.csv")
print(vocab2)
print("単語数:"+str(len(vocab2)))
pd.DataFrame(vocab2).to_csv(col_name2+"単語リスト.csv")
print("単語リストが仮想ファイルに「単語リスト.xlsx」として保存されました")

4.課題軸と解決手段軸それぞれについて軸の言葉を指定+GuidedLDA適用

軸の言葉していが面倒だっため、適当に選択。

#@title ←【STEP3】課題側_半教師有りLDA実行
#ほんとはここのワードリストの指定が大事
topic0_subj = ",".join(vocab[51:60])
topic1_subj = ",".join(vocab[61:70])
topic2_subj = ",".join(vocab[71:80])
topic3_subj = ",".join(vocab[81:90])
topic4_subj = ",".join(vocab[91:100])
topic5_subj = ",".join(vocab[101:110])
topic6_subj = ",".join(vocab[111:120])

input_topic0 = topic0_subj.split(",")
input_topic1 = topic1_subj.split(",")
input_topic2 = topic2_subj.split(",")
input_topic3 = topic3_subj.split(",")
input_topic4 = topic4_subj.split(",")
input_topic5 = topic5_subj.split(",")
input_topic6 = topic6_subj.split(",")

topic_list = [input_topic0
               ,input_topic1
               ,input_topic2
               ,input_topic3
               ,input_topic4
               ,input_topic5]

seed_topic_list = []
for k,topic in enumerate(topic_list):
    if topic[0]=="":
        pass
    else:
        seed_topic_list.append(topic)

#topic数は指定トピック数+1
num_topic = len(seed_topic_list)+1

s_conf = 0.12 #@param {type:"slider", min:0, max:1, step:0.01}
model = guidedlda.GuidedLDA(n_topics=num_topic, n_iter=100, random_state=7, refresh=20)
seed_topics = {}
for t_id,st in enumerate(seed_topic_list):
    for word in st:
        seed_topics[word2id[word]] = t_id

model.fit(X,seed_topics=seed_topics,seed_confidence=s_conf)
docs = model.fit_transform(X,seed_topics={},seed_confidence=s_conf)
print(docs)

print("結果------学習後のtopic毎の代表的な言葉----------------------------------------")
print("最後のtopicは自動的に挿入された「その他」のトピック----------------------------")
dic = out1(model,vocab)

print("各出願へのtopic割当結果--------------------------------------------------------")
print("")
df["no"]=df.index.tolist()
df['LDA結果_subj'] = df["no"].apply(lambda x:"topic" + str(docs[x].argmax()))
df[["出願番号","LDA結果_subj"]]
df['LDA結果_subj'] = df['LDA結果_subj'].replace(dic)

さらに解決手段の軸も同様に処理する。

5.付与結果をクロス集計(集計後のマスには出願番号を挿入)

ct = pd.crosstab(df['LDA結果_kai'], df['LDA結果_subj'], df['出願番号'], aggfunc=','.join)
ct

結果↓
工夫した点としては、そのままの出力では、軸名がtopic●と出てしまうので、そのトピックに含まれる代表的なワード上位10位を出力させるようにした点。

件数を表示させたい場合は以下

ct = pd.crosstab(df['LDA結果_kai'], df['LDA結果_subj'], df['出願番号'], aggfunc=np.size)

4.性能評価

さんざんだった・・・
適当にワードをまとめあげてるので、次はちゃんと人間が作ったマップを再現できるか試さないと。
あとなんかコードが冗長な気がする(2軸処理するため)ので、もっと簡潔に書く方法要検討。