张飞比关羽还能打?一位酷爱三国的日本程序员,用NLP分析了武将们的战斗力
大数据文摘出品
来源:Qiita
编译:李欣月、刘俊寰
作为中国四大名著之一,三国的故事自然备受国人喜爱和追捧,但是谁又能想到三国竟然在日本也“出了圈”,举个例子,吴宇森导演的电影《赤壁》在日本的票房收入超过国内,同时该电影也是日本影史上票房最高的华语电影。
不仅在影视行业,三国因为其同时具有历史、策略、动作、人物等众多元素,在日本游戏行业的发展势头也相当迅猛,其中日本光荣株式协会就开发了包括《三国志》在内的一系列游戏,备受好评。
人物是三国故事经久不衰的关键因素之一,出场的众多复杂人物到底孰优孰劣成为三国游戏得以建立的前提,但是从小说本身来看,它在文本意义上果真如此吗?
怀着这样的疑问,一个日本小哥哥(@youwht)就利用AI对三国中的人物进行了一次深扒,通过“自然语言处理”和“机器学习”来分析三国文本,更新了三国游戏中的武将排名,得出了很有趣的结论,比如,张飞比关羽还能打,武力值高居榜首,而在“政治谋略”上,诸葛亮则比两大主公——曹操和刘备都更胜一筹,而姜维作为诸葛亮的传人,也被分析出是最接近诸葛亮的人物。
不仅分析出的结果好玩,这位作者的语言风格也非常有意思,代码外还有各种人物内心戏乱入,简直还原了一个懂AI的三国小剧场。
大数据文摘试图原文还原这篇有趣的blog,一起看看,这位热爱三国的日本程序员,是如何用AI帮三国武将们重新徘名的。
有人只看图就能分辨出谁是谁吗?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
比较接近诸葛亮的人是谁?
刘备拥有关羽,那么曹操拥有谁呢?
孙权有鲁肃辅佐,刘备又对应谁呢?
将吉川英治《三国》中出现的特有名称进行形态分析后以单词为单位区隔开。
区隔后的结果用Word2Vec进行矢量化。(Word2Vec:能够以N维矢量表现单词,并进行加法运算等运算的技术。提前阅读一下 “陌生人”的反义词是“白色恋人”这篇文章可能会更好理解。附链接:https://qiita.com/youwht/items/f21325ff62603e8664e6)
此时这些武将已经被矢量化了,如果要从这些矢量中得出相关性高的矢量(复数矢量的集合体),需要多少个算式才能计算出接近KOEI三国志的参数呢?其实最大的难关是第一步的形态分析,因此必须尽可能正确地去认识三国。
韩玄,刘度,赵范,金旋(AI知道我们荆州四杰么?)
玄德=刘玄德=刘备玄德⇒“刘备”
Colaboratory:在浏览器上可以免费使用的Python运行环境
Janome:环境构建轻松的形态分析器
Word2Vec:将自然语言数值化/矢量化的模型
GoogleDriveのマウント
# これを実行すると、認証用URLが表示されて、キーを入力すると
# 「drive/My Drive/」の中に、認証したアカウントのgoogle driveが入るfrom google.colab import drivedrive.mount('/content/drive')
Janomeのインストール
!pip install janome
Janomeで形態素解析(名詞・動詞の抽出)
#素状態のJanomeの性能を確認する
# Janomeのロードfrom janome.tokenizer import Tokenizer
# Tokenneizerインスタンスの生成 tokenizer = Tokenizer()
# テキストを引数として、形態素解析の結果、名詞・動詞原型のみを配列で抽出する関数def extract_words(text):
tokens = tokenizer.tokenize(text)
return [token.base_form for token in tokens
if token.part_of_speech.split(',')[0] in['名詞', '動詞']]
sampletext = u"文章の中から、名詞、動詞原型などを抽出して、リストにするよ"print(extract_words(sampletext))sampletext = u"劉備と関羽と張飛の三人は桃園で義兄弟の契りを結んだ"print(extract_words(sampletext))sampletext = u"悪来典韋はかえって、許褚のために愚弄されたので烈火の如く憤った"print(extract_words(sampletext))
実行結果
['文章', '中', '名詞', '動詞', '原型', '抽出', 'する', 'リスト', 'する']['劉', '備', '関', '羽', '張', '飛', '三', '人', '桃園', '義兄弟', '契り', '結ぶ']['典', '韋', '許', '褚', 'ため', '愚弄', 'する', 'れる', '烈火', '憤る']
janome+neologdのインストール
#結構時間がかかる(6分くらい)
#Mydrive上の、先程のjanome+neologdのパスを指定する
#最新版とファイル名が一致しているかどうかは各自で確認すること!pip install "drive/My Drive/Janome-0.3.9.neologd20190523.tar.gz" --no-compile
インストール実行結果の末尾
#WARNING: The following packages were previously imported in this runtime:
# [janome]
#You must restart the runtime in order to use newly installed versions.
NEologd入れた状態で形態素解析する
# Janomeのロードfrom janome.tokenizer import Tokenizer
# Tokenneizerインスタンスの生成 ★ここが異なる★tokenizer = Tokenizer(mmap=True)
# テキストを引数として、形態素解析の結果、名詞・動詞原型のみを配列で抽出する関数def extract_words(text):
tokens = tokenizer.tokenize(text)
return [token.base_form for token in tokens
if token.part_of_speech.split(',')[0] in['名詞', '動詞']]
sampletext = u"劉備と関羽と張飛の三人は桃園で義兄弟の契りを結んだ"print(extract_words(sampletext))sampletext = u"悪来典韋はかえって、許褚のために愚弄されたので烈火の如く憤った"print(extract_words(sampletext))sampletext = u"田豊。沮授。許収。顔良。また――審配。郭図。文醜。などという錚々たる人材もあった。"print(extract_words(sampletext))sampletext = u"第一鎮として後将軍南陽の太守袁術、字は公路を筆頭に、第二鎮、冀州の刺史韓馥、第三鎮、予州の刺史孔伷、第四鎮、兗州の刺史劉岱、第五鎮、河内郡の太守王匡、第六鎮、陳留の太守張邈、第七鎮、東郡の太守喬瑁"print(extract_words(sampletext))
実行結果
['劉備', '関羽', '張飛', '三', '人', '桃園', '義兄弟', '契り', '結ぶ']['悪来', '典韋', '許褚', 'ため', '愚弄', 'する', 'れる', '烈火', '憤る']['田豊', '沮授', '許', '収', '顔良', '審配', '郭図', '文醜', '錚々たる', '人材', 'ある']['鎮', '後将軍', '南陽', '太守', '袁術', '字', '公路', '筆頭', '二', '鎮', '冀州', '刺史', '韓', '馥', '三', '鎮', '予州', '刺史', '孔', '伷', '四',
人名リストの読み込み
#人物の名前が列挙してあるテキストから、ワードリストを作成するimport codecsdef getKeyWordList():
input_file = codecs.open('drive/My Drive/Sangokusi/三国志_人名リスト.txt' , 'r', 'utf-8')
lines = input_file.readlines() #読み込み result_list = []
for line in lines:
tmp_line = line
tmp_line = tmp_line.replace("\r","")
tmp_line = tmp_line.replace("\n","")
#ゴミデータ削除のため、2文字以上のデータを人名とみなす if len(tmp_line)>1:
result_list.append(tmp_line)
return result_list
jinbutu_word_list = getKeyWordList()print(len(jinbutu_word_list))print(jinbutu_word_list[10:15])
実行結果
1178['張楊', '張虎', '張闓', '張燕', '張遼']
因为马忠同名同姓,所以放弃了区分;
“乔瑁”在wiki上不存在所以之后追加;
“張繍”“張繡”微妙的字体不同;
“祝融夫人”变更为“祝融”。
Janomeのユーザ辞書csvの作成
#作成したキーワードリストから、janomeのユーザ辞書形式となるCSVファイルを作成するkeyword_list = jinbutu_word_listuserdict_list = []
#janomeのユーザ辞書形式に変換をかける。コストや品詞の設定等for keyword in keyword_list:
#「表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音」 #参考:http://taku910.github.io/mecab/dic.html #コストは,その単語がどれだけ出現しやすいかを示しています. #小さいほど, 出現しやすいという意味になります. 似たような単語と 同じスコアを割り振り, その単位で切り出せない場合は, 徐々に小さくしていけばいい
userdict_one_str = keyword + ",-1,-1,-5000,名詞,一般,*,*,*,*," + keyword + ",*,*"
#固有名詞なので、かなりコストは低く(その単語で切れやすく)設定 userdict_one_list = userdict_one_str.split(',')
userdict_list.append(userdict_one_list)
print(userdict_list[0:5])
#作成したユーザ辞書形式をcsvでセーブしておくimport csvwith open("drive/My Drive/Sangokusi/三国志人名ユーザ辞書.csv", "w", encoding="utf8") as f:
csvwriter = csv.writer(f, lineterminator="\n") #改行記号で行を区切る csvwriter.writerows(userdict_list)
実行結果
[['張譲', '-1', '-1', '-5000', '名詞', '一般', '*', '*', '*', '*', '張譲', '*', '*'], ['張角', '-1', '-1', '-5000', '名詞', '一般', '*', '*', '*',
这样,记载了1000位以上有名武将的用户词典就制作出来了!
现在让我们来检验一下这本词典的适用范围。
ユーザ辞書を使った場合
# Janomeのロードfrom janome.tokenizer import Tokenizer
#ユーザ辞書、NEologd 両方使う。★ここが変更点★tokenizer_with_userdict = Tokenizer("drive/My Drive/Sangokusi/三国志人名ユーザ辞書.csv", udic_enc='utf8', mmap=True)
# テキストを引数として、形態素解析の結果、名詞・動詞原型のみを配列で抽出する関数def extract_words_with_userdict(text):
tokens = tokenizer_with_userdict.tokenize(text)
return [token.base_form for token in tokens
#どの品詞を採用するかも重要な調整要素 if token.part_of_speech.split(',')[0] in['名詞', '動詞']]
sampletext = u"劉備と関羽と張飛の三人は桃園で義兄弟の契りを結んだ"print(extract_words_with_userdict(sampletext))sampletext = u"悪来典韋はかえって、許褚のために愚弄されたので烈火の如く憤った"print(extract_words_with_userdict(sampletext))sampletext = u"田豊。沮授。許収。顔良。また――審配。郭図。文醜。などという錚々たる人材もあった。"print(extract_words_with_userdict(sampletext))sampletext = u"第一鎮として後将軍南陽の太守袁術、字は公路を筆頭に、第二鎮、冀州の刺史韓馥、第三鎮、予州の刺史孔伷、第四鎮、兗州の刺史劉岱、第五鎮、河内郡の太守王匡、第六鎮、陳留の太守張邈、第七鎮、東郡の太守喬瑁"print(extract_words_with_userdict(sampletext))
実行結果
['劉備', '関羽', '張飛', 'の', '三', '人', '桃園', '義兄弟', '契り', '結ぶ']['悪来', '典韋', '許褚', 'ため', '愚弄', 'する', 'れる', '烈火', '憤る']['田豊', '沮授', '許', '収', '顔良', '審配', '郭図', '文醜', '錚々たる', '人材', 'ある']['鎮', '後将軍', '南陽', '太守', '袁術', '字', '公路', '筆頭', '二', '鎮', '冀州', '刺史', '韓馥', '三', '鎮', '予州', '刺史', '孔伷', '四', '鎮', '兗
あざなCSVの読み込み
import csv
csv_file = open("drive/My Drive/Sangokusi/三国志_あざな変換リスト.csv", "r", encoding="utf8", errors="", newline="" )#リスト形式azana_reader = csv.reader(csv_file, delimiter=",", doublequote=True, lineterminator="\r\n", quotechar='"', skipinitialspace=True)azana_list = [ e for e in azana_reader ]csv_file.close()
print(len(azana_list))print(azana_list[2])
#全員の字リストを作るのは難しかったが、
#['雲長', '関羽']のような132人の代表的な字とその対比表が入っている
実行結果
132['雲長', '関羽']
字(あざな)の変換処理の実装
#これは、字(あざな)を置き換えるだけの単純な置換処理def azana_henkan(input_text):
result_text = input_text
for azana_pair in azana_list:
result_text = result_text.replace(azana_pair[0],azana_pair[1])
return result_text
#単純に、字からの変換をかけるだけだと、
#趙雲子龍→趙雲趙雲などのようになる場合が多いため、
#同一の人物名で重複している場合は、一方を削除する。
#また、劉玄徳、趙子龍、などのような表現に対応するため、
#フルネームで2文字の場合はAAB→AB(劉玄徳→劉劉備→劉備)
#フルネームで3文字の場合はAAB→AB(諸葛孔明→諸葛諸葛亮→諸葛亮)
# となる名寄せを行う。
#(※名字1文字+名前二文字はあまり居ない気がするので無視)def jinmei_tyouhuku_sakujyo(input_text):
jinbutu_word_list = getKeyWordList()
result_text = input_text
for jinbutumei in jinbutu_word_list:
result_text = result_text.replace(jinbutumei+jinbutumei, jinbutumei)
if len(jinbutumei) == 2:
result_text = result_text.replace(jinbutumei[0]+jinbutumei, jinbutumei)
if len(jinbutumei) == 3:
result_text = result_text.replace(jinbutumei[0]+jinbutumei[1]+jinbutumei, jinbutumei)
return result_text
sampletext = u"これは予州の太守劉玄徳が義弟の関羽字は雲長なり"print(jinmei_tyouhuku_sakujyo(azana_henkan(sampletext)))sampletext = u"趙子龍は、白馬を飛ばして、馬上から一気に彼を槍で突き殺した。"print(jinmei_tyouhuku_sakujyo(azana_henkan(sampletext)))sampletext = u"趙雲子龍も、やがては、戦いつかれ、玄徳も進退きわまって、すでに自刃を覚悟した時だった。"print(jinmei_tyouhuku_sakujyo(azana_henkan(sampletext)))
実行結果
これは予州の太守劉備が義弟の関羽字は関羽なり趙雲は、白馬を飛ばして、馬上から一気に彼を槍で突き殺した。趙雲も、やがては、戦いつかれ、劉備も進退きわまって、すでに自刃を覚悟した時だった。
SlothLibからのデータの取得&リスト化
#雑音になりやすい単語(「彼」など)はストップワードとして除外する
#SlothLibのテキストを使う。
#どんな言葉が除外されるのかは、直接URLを見れば良い
#参考: http://testpy.hatenablog.com/entry/2016/10/05/004949import urllibslothlib_path = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt'#slothlib_file = urllib2.urlopen(slothlib_path) #←これはPython2のコードslothlib_file = urllib.request.urlopen(slothlib_path)slothlib_stopwords = [line.decode("utf-8").strip() for line in slothlib_file]slothlib_stopwords = [ss for ss in slothlib_stopwords if not ss==u'']
#['彼','彼女',・・・]のようなリストになるprint(len(slothlib_stopwords))print(slothlib_stopwords[10:15])
実行結果
310['いま', 'いや', 'いろいろ', 'うち', 'おおまか']
鶏肋ワードの除去機能を実装する
sampletext = u"彼は予州の太守劉玄徳が義弟の関羽字は雲長。彼は劉備玄徳の義兄弟だ"
tmp_word_list = extract_words_with_userdict(jinmei_tyouhuku_sakujyo(azana_henkan(sampletext)))
print(tmp_word_list)
#このようにして、単語リストからストップワードを除外するtmp_word_list = [word for word in tmp_word_list if word not in slothlib_stopwords]
print(tmp_word_list)
実行結果
['彼', '予州', '太守', '劉備', '義弟', '関羽', '字', '関羽', 'なり', '彼', '劉備', 'の', '義兄弟']['予州', '太守', '劉備', '義弟', '関羽', '関羽', 'なり', '劉備', 'の', '義兄弟']
self.resulttext=re.sub(r'※[#.*?1-88-37.*?]',"瓚",self.resulttext)
全文テキストに対して、字(あざな)変換処理をかける
import codecsdef azana_henkan_from_file(input_file_path):
input_file = codecs.open(input_file_path, 'r', 'utf-8')
lines = input_file.readlines() #読み込み result_txt = ""
for line in lines:
result_txt += line
result_txt = azana_henkan(result_txt)
return result_txt
#ファイル生成用関数定義
#mesのテキストを、filepathに、utf-8で書き込むdef printFile(mes,filepath):
file_utf = codecs.open(filepath, 'w', 'utf-8')
file_utf.write(mes)
file_utf.close()
return "OK"
azana_henkango_zenbun = azana_henkan_from_file('drive/My Drive/Sangokusi/三国志全文.txt')azana_henkango_zenbun = jinmei_tyouhuku_sakujyo(azana_henkango_zenbun)
printFile(azana_henkango_zenbun,'drive/My Drive/Sangokusi/三国志全文_あざな変換済み.txt')
全文の形態素解析
%%time#全文分解するのに10分ほどかかるimport codecs# ['趙雲', '白馬', '飛ばす', '馬上', '彼', '槍', '突き', '殺す'] このようなリストのリスト(二次元リスト)になるdef textfile2wordlist(input_file_path):
input_file = codecs.open(input_file_path, 'r', 'utf-8')
lines = input_file.readlines() #読み込み result_word_list_list = []
for line in lines:
# 1行ずつ形態素解析によってリスト化し、結果格納用のリストに格納していく # Word2Vecでは、分かち書きされたリスト=1文ずつ、のリストを引数にしている tmp_word_list = extract_words_with_userdict(line)
#別途準備しておいたstopワードリストを使って除外処理を行う tmp_word_list = [word for word in tmp_word_list if word not in slothlib_stopwords]
result_word_list_list.append(tmp_word_list)
return result_word_list_list
Word_list_Sangokusi_AzanaOK_with_userdict_neologd = textfile2wordlist('drive/My Drive/Sangokusi/三国志全文_あざな変換済み.txt')
#作成したワードリストは、pickleを使って、GoogleDriveに保存しておく(一回10分くらいかかるからね)import picklewith open('drive/My Drive/Sangokusi/Word_list_Sangokusi_AzanaOK_with_userdict_neologd_V4.pickle', 'wb') as f:
pickle.dump(Word_list_Sangokusi_AzanaOK_with_userdict_neologd, f)
#保存したpickleファイルは、以下のように復元するwith open('drive/My Drive/Sangokusi/Word_list_Sangokusi_AzanaOK_with_userdict_neologd_V4.pickle', 'rb') as f:
Word_list_Sangokusi_AzanaOK_with_userdict_neologd = pickle.load(f)
print(len(Word_list_Sangokusi_AzanaOK_with_userdict_neologd))print(Word_list_Sangokusi_AzanaOK_with_userdict_neologd[10:20])
张飞比关羽还能打?一位酷爱三国的日本程序员,用NLP分析了武将们的战斗力相关推荐
- 张飞比关羽还能打?一位酷爱三国的日本程序员,用NLP分析了武将们的战斗力...
大数据文摘出品 来源:Qiita 编译:李欣月.刘俊寰 作为中国四大名著之一,三国的故事自然备受国人喜爱和追捧,但是谁又能想到三国竟然在日本也"出了圈",举个例子,吴宇森导演的电影 ...
- 奇偶位交换 牛客网 程序员面试金典 C++ Python
奇偶位交换 牛客网 程序员面试金典 C++ Python 题目描述 请编写程序交换一个数的二进制的奇数位和偶数位.(使用越少的指令越好) 给定一个int x,请返回交换后的数int. 测试样例: 10 ...
- 一位从业20年的程序员分享积累的20条编程经验
编者按:原文作者乔纳森·丹尼可(Jonathan Danylko)是一位自由职业的web架构师和程序员,编程经验已超过20年,涉足领域有电子商务.生物技术.房地产.医疗.保险和公用事业.正如乔纳森在文 ...
- 【推荐一位Python大佬】 从程序员到创业者,再到自由职业
今天要推荐的公众号叫 Python绿色通道 是一位混迹职场近10年的老鸟. 下面文字是他的简单口述. 众所周知湖北黄冈在国内的教育还算不错,而我就是来自黄冈的,我的朋友们都叫我龙总. 感觉霸气一点. ...
- 15 位健在的牛叉程序员,你知道哪几位?
现如今程序员是越来越多了,所以优秀的程序员也是越来越多了.但是他们中哪些才是顶尖中的顶尖呢? 其实要明确地指出"哪一个"程序员是最优秀的,这几乎不太可能,但是业内的开发者们还是对这 ...
- “我辞退了一位学位学历造假的程序员“
[CSDN 编者按]这个社会的竞争越来越激烈,为了能让自己得到一份高薪的工作,很多人都选择了走捷径,弄一份假的简历去面试,觉得这样更容易通过招聘,找到好工作.可简历造假可是职场禁忌,严重可能会断送自己 ...
- 一位工作一年的程序员的2021年度总结
hello,大家好啊,我是一名拥有一年开发经验的java开发程序员, 2021年就要过去了,大家有没有什么感慨呢,为了迎接新的2022年, 我做了一篇2021年的个人总结, 希望总结经验,突破未来 工 ...
- 还在996?等着进ICU?程序员不服!
拒绝996,远离ICU https://996.icu (识别下方二维码可打开网址) 近日,网友建立网站 996.ICU 抵制互联网公司违规实行的 996 工作制,并曝光部分公司.同时,Github ...
- 程序猿如何变为创业狗?一位刚融到资的程序员含泪述说
点击上方"程序人生",选择"置顶公众号" 第一时间关注程序猿(媛)身边的故事 图片源自:Silicon Valley Season 1 作者 五五 白天搬砖,晚 ...
最新文章
- Java、Android静态代理与动态代理
- 第11章:项目风险管理(2)—章节重点
- 9102 BITRUN Hackathon is COMING!
- String方法(一)
- 经典技术文章翻译(3):IIS7协议侦听器(Introducing IIS 7 IIS Protocol Listeners)
- Matlab 马尔可夫链预测双色球
- 替换空格——python
- setcpu_cpuset子系统
- 全网首发:12306抢票算法大曝光?(十张图搞定)
- 苹果手机如何深度清理_安卓手机必备清理软件APP,完全免费超级深度清理
- 关于Element-ui中el-table在谷歌和360极速浏览器的兼容问题
- 一文了解销售软件产品的增值税即征即退政策
- win10计算机打印机共享怎么设置方法,Win10系统怎么设置打印机共享?Win10系统打印机共享设置教程...
- Spring Boot 入门系列(二十三)整合Mybatis,实现多数据源配置!
- Servlet中forward和redirect的区别(转)
- 3.0时代,公关传播在人文营销中的应用
- 政客常用手段_我可以比政客更好地管理经济
- 计算机基础-程序设计基础
- 中山联禾科技推出永宏FB PLC数据采集模块
- 虚拟创业云|BBC幼儿英语启蒙动画Alphablocks字母积木全4季
热门文章
- 编译原理期末复习资料
- 思考笔记--Web3与现代经济模型
- 小米开源文件管理器MiCodeFileExplorer-源码研究(6)-媒体文件MediaFile和文件类型MimeUtils
- 怎样修改日立uax规格表_UAX型电梯调试手册
- ios 拦截html请求参数,iOS UIWebView URL拦截
- 高通中国区董事长孟樸:高通将用服务器撬动物联网大市场
- Iintel CPU 漏洞科普和分析
- HTML5合并单元格居中,excel表格合并后居中的方法步骤
- vue动态form表单实现
- 发布 RT-Thread 软件包 bc28_mqtt