前回 #051 は空白を単語の区切りとみなしていたが、hand-written rules
などの -
でつながった単語がある場合、-
も単語の区切りを表しているよな? と今回解きながら思った。
あと、( ) , . "
などの記号も単語に含めるべきではないのではないだろうか。
しかし、'
についてはちょっと厄介で、'conceptual ontologies'
の '
は除きたいけど、Moore's Law
の '
はこのまま置いておきたい。
…という部分も今回実装した。
言語処理100本ノック #052
52. ステミング
51の出力を入力として受け取り,Porterのステミングアルゴリズムを適用し,単語と語幹をタブ区切り形式で出力せよ.
stemming の準備
参考 👉 Installing Packages — Python Packaging User Guide
pip に setuptools と wheel projects を入れる。
$ python -m pip install --upgrade pip setuptools wheel
stemming をインストールする。
$ pip install stemming
確認。
$ pip list Package Version ----------------- ------- ... ... stemming 1.0.1 ... ...
stemming の使い方
from stemming.porter2 import stem
でインポートし、stem(単語)
で語幹が得られる。
参考 👉 Porterのステミングアルゴリズムをpythonで使う - kzk0829’s blog
完成
記号を取り除く関数内で、 Moore's Law の '
は残したいので、
's
というパターンの場合は '
を取り除かないようにした。
でもこれやと、rock 'n' roll の '
は取り除かれてまうんやなあ(今回解析する文章に入ってないし、まあええか)。
# knock_052.py from stemming.porter2 import stem nlp_file = 'nlp.txt' def get_sentence(line): sentence = '' for i, char in enumerate(line): if char in ['.', ';', ':', '?', '!']: sentence += char if i+3 > len(line): # 文末文字 -> 改行 yield sentence sentence = '' elif line[i+1] == ' ': if str.isupper(line[i+2]): # 文末文字 -> 空白 -> 英大文字 yield sentence sentence = '' elif char == ' ' and len(sentence) <1: # 文の間の空白は読み飛ばす pass else: sentence += char if len(sentence) > 1: # タイトル行の処理はここで pass def file_to_sentence(readfile): with open(readfile, encoding='utf-8') as f: for line in f: for sentence in get_sentence(line): yield sentence # 記号を除くやつ def remove_marks(word): removed = '' for i, c in enumerate(word): if c in ['.', ';', ':', '?', '!', \ ',', '(', ')', '"']: pass elif c in ["'"]: if i+1 < len(word): if word[i+1] in ['s']: removed += c else: pass else: pass else: removed += c return removed if __name__ == '__main__': for sentence in file_to_sentence(nlp_file): for word in re.split(' |-', sentence): # 空白または - で文を区切る rword = remove_marks(word) if len(rword) > 1: print(rword, '\t', stem(rword)) print('\n', end='')
結果
# python knock_052.py >> knock_052.txt (30行目まで) Natural Natur language languag processing process NLP NLP is is field field of of computer comput science scienc artificial artifici intelligence intellig and and linguistics linguist concerned concern with with the the interactions interact between between computers comput and and human human natural natur languages languag As As such such NLP NLP is is related relat to to
ステミングについてちょっと調べてみた
参考 👉 NLP: A quick guide to Stemming - Tushar Srivastava - Medium
概要
ステミングは、inflectional forms (活用形) から suffix (接尾辞) を除いて root word (語根) や stem word (語幹) にすること。
英語では、文法的なカテゴリの違いを表現するために語を活用させる。文法カテゴリというのは、 テンス(時制)やケース(格)、ウォイス(態)、アスペクト(相)、人称、数、性、ムード(話し手の気持ちや主観的態度)、有生性、定性などのこと。
活用した語を元に戻してやること(=ステミング)により、自然言語処理や言語研究の際に同義語や類義語が探しやすくなるわけ。
たとえば、動詞の活用形を stemming によりステミングすると次のようになる。
# test_stemming.py word stem ------- ---- wait wait waits wait waited wait waiting wait
綴りが変わることもある。
- beauty, duty + -ful → beautiful, dutiful (-y changes to i)
- heavy, ready + -ness → heaviness, readiness (-y changes to i)
- able, possible + -ity → ability, possibility (-le changes to il)
- permit, omit + -ion → permission, omission (-t changes to ss)
stemming では、1, 2 はうまくいっているが、3, 4 はうまくいっていないっぽい。
# test_stemming.py word stem ---------- ---- beauty beauti beautiful beauti ---------- ---- heavy heavi heaviness heavi ---------- ---- able abl ability abil ---------- ---- permit permit permission permiss
ちなみに、語根と語幹の違いは、語根のほうが小さい要素。 複合語の語幹は、複数の語根からなる(たとえば、wheelchairs -> wheelchair (stem) = wheel (root) + chair (root))。 stemming の出力は語幹のほう。
# test_stemming.py word stem ---------- ---- wheelchairs wheelchair
Over Stemming
異なる語幹をもつ語が、同じ語幹にステミングされてしまうこと。
# test_stemming.py word stem ---------- ---- universal univers university univers universe univers
これらの語は、語源は関連しているが、現代での意味が大きく異なるので、 自然言語処理において類義語として扱わないほうが良いかも。
Under Stemming
同じ語幹にステミングされるべき語が、異なる語幹にステミングされてしまうこと。
# test_stemming.py word stem ---------- ---- alumnus alumnus alumni alumni alumnae alumna ---------- ---- have have had had has has having have
難しいですね。
ステミングツールは、今回使用した stemming 以外にも色々ありそうなので、精度を上げたい場合には他のツールを使ってみるのも良いかもしれない。 上に書いた参考リンクの後半に、ステミングツールの紹介がある。
調べるのに使ったコード
# test_stemming.py from stemming.porter2 import stem wordlist = ['wait', 'waits', 'waited', 'waiting', \ 'beauty', 'beautiful', \ 'heavy', 'heaviness', \ 'able', 'ability', \ 'permit', 'permission', \ 'have', 'had', 'has', 'having', \ 'universal', 'university', 'universe', \ 'alumnus', 'alumni', 'alumnae', \ 'wheelchairs'] print('word', '\t', 'stem') print('----------', '\t', '-------') for w in wordlist: print(w, '\t', stem(w))