Python & NLTKで英語圏の名前から性別を判定してみる
新年明けましておめでとうございます。
大晦日前のブログ更新を実現出来なかったうえピーです。
2020年、オリンピックYearということで、「入門 自然言語処理」で学んだ”名前性別分類機”を拡張して、弊社も海外からのインバウンドに備えたいと思います。
今回参照して頂くのは、6章 テキスト分類の学習 6.1.1 性別の判定となります。
まずは、素直に参考書の通り、末尾の1文字で性別を分類してみましょう。
今回は、前回よりも機械学習らしさを出すために、サンプルデータを学習データ、検証データ、本番データに分けて検証します。
01: import nltk
02: from nltk.corpus import names
03: import random
04: names = ([(name , ‘male’) for name in names.words(‘male.txt’)] + [(name, ‘female’) for name in names.words(‘female.txt’)])
05: random.shuffle(names)
06: def gender_features(word):
07: return {‘last_letter’: word[-1]}
08: featuresets = [(gender_features(n), g) for (n, g) in names]
09: train_set, test_set, st_set = featuresets[1000:], featuresets[:500], featuresets[500:1000]
10: classifier = nltk.NaiveBayesClassifier.train(train_set)
11: nltk.classify.accuracy(classifier, test_set)
12: nltk.classify.accuracy(classifier, st_set)
簡単にプログラムの説明をしておきます。
01~03は必要なライブラリのimportです。
04~05、今回のお名前サンプルデータはnltk.corpus.namesを使用して、データにランダム性を出すためシャッフルしています。
06~07は、素性抽出器で、末尾の1文字見れば、性別大体判別できるっしょ、って感じです。
08~、後は抽出した素性と性別の組み合わせデータを学習データ、検証データ、本番データに分けて、学習及び評価をしています。
さて、その結果は・・・・・
nltk.classify.accuracy(classifier, test_set)
Out[17]: 0.764
nltk.classify.accuracy(classifier, st_set)
Out[18]: 0.772
約77%で正しく判別できてます。なるほど、大抵の英語名は末尾で判断できるんですね。
それでは、もっと上を目指しましょう!!
どんな素性があるかな・・・・
そうだ!!末尾を2文字にするのはどうでしょう?
13: def gender_features2(word):
14: return {‘last_letter’: word[-2]}
15: featuresets2 = [(gender_features2(n), g) for (n, g) in names]
16: train_set2, test_set2, st_set2 = featuresets2[1000:], featuresets2[:500], featuresets2[500:1000]
17: classifier2 = nltk.NaiveBayesClassifier.train(train_set2)
18: nltk.classify.accuracy(classifier2, test_set2)
19: nltk.classify.accuracy(classifier2, st_set2)
こんな感じでえいっと。
nltk.classify.accuracy(classifier2, test_set2)
Out[19]: 0.7
nltk.classify.accuracy(classifier2, st_set2)
Out[20]: 0.664
げっ、認識率落ちた、、、まじか。
うーん、じゃあ、末尾1文字と先頭1文字の組み合わせはどうだろう?
20: def gender_features3(word):
21: return {‘last_letter’: word[-1],
22: ‘first_letter’: word[0]}
23: featuresets3 = [(gender_features3(n), g) for (n, g) in names]
24: train_set3, test_set3, st_set3 = featuresets3[1000:], featuresets3[:500], featuresets3[500:1000]
25: classifier3 = nltk.NaiveBayesClassifier.train(train_set3)
26: nltk.classify.accuracy(classifier3, test_set3)
27: nltk.classify.accuracy(classifier3, st_set3)
nltk.classify.accuracy(classifier3, test_set3)
Out[21]: 0.796
nltk.classify.accuracy(classifier3, st_set3)
Out[22]: 0.798
おっ、ちょっと上がった!!!
よし、目標を80%越えに設定してもう一声。
じゃあ、長さも追加してみよっと。
28: def gender_features4(word):
29: return {‘last_letter’: word[-1],
30: ‘first_letter’: word[0],
31: ‘length’: len(word)}
32: featuresets4 = [(gender_features4(n), g) for (n, g) in names]
33: train_set4, test_set4, st_set4 = featuresets4[1000:], featuresets4[:500], featuresets4[500:1000]
34: classifier4 = nltk.NaiveBayesClassifier.train(train_set4)
35: nltk.classify.accuracy(classifier4, test_set4)
36: nltk.classify.accuracy(classifier4, st_set4)
nltk.classify.accuracy(classifier4, test_set4)
Out[23]: 0.788
nltk.classify.accuracy(classifier4, st_set4)
Out[24]: 0.796
ぶっちゃけ、長さは効果ないな。
あきらめない、8割超える!!!!!
・・・・・
Take5、先頭文字を2桁、、、ダメ、、、
・・・・・
Take6、末尾と長さの組み合わせ、、、ダメ、、、
・・・・・
・・・・・
Take7、Take8、Take9、、、、Take13おっ!!!
37: import math
38: def gender_features13(word):
39: return {‘last_letter’: word[-1],
40: ‘middle_letter’: word[math.floor(len(word)/2):math.floor(len(word)/2)+2],
41: ‘length’: len(word),
42: ‘first_letter’: word[0]}
43: featuresets13 = [(gender_features13(n), g) for (n, g) in names]
44: train_set13, test_set13, st_set13 = featuresets13[1000:], featuresets13[:500], featuresets13[500:1000]
45: classifier13 = nltk.NaiveBayesClassifier.train(train_set13)
46: nltk.classify.accuracy(classifier13, test_set13)
47: nltk.classify.accuracy(classifier13, st_set13)
nltk.classify.accuracy(classifier13, test_set13)
Out[27]: 0.814
nltk.classify.accuracy(classifier13, st_set13)
Out[28]: 0.82
末尾に中間文字も加えて、長さと先頭文字。ごっちゃ混ぜで出たぜ、80%越え!!!
さぁ、皆さんに機械学習結構地味、、、ってわかって頂けたところで、See You。