Python 3でのロシア語テキストの品詞の定義(POSタグ)

これらの柔らかいフレンチロールを食べて、はいお茶を飲んでください。 」、各単語の品詞を決定する必要があります:



[('', '.'), ('', '.'), ('', '. .'), ('', '.'), ('', '.'), ('', '.'), ('', ''), ('', '.'), ('', '.')]







なぜこれが必要なのですか? たとえば、ブログ投稿のタグを自動的に識別する(名詞の選択用)。 形態学的マークアップは、コンピューターテキスト分析の最初の段階の1つです。



既存のソリューション



もちろん、すべてが私たちの前にすでに発明されています。 Yandexのmystem 、ロシア語をサポートするTreeTagger 、pythonにはnltkkmikeからのpymorphyがあります。 これらのユーティリティはすべて正常に動作しますが、pymorphyにはpython 3のサポートがなく、nltkにはpythonの3番目のバージョンのベータサポートしかありません(そして常に何かが永遠に落ちます)。 しかし、モジュールを作成するための本当の目標は、形態学的アナライザーがどのように機能するかを理解することです。



アルゴリズム



まず、単語がどの品詞を指すかを普通の人がどのように決定するかを理解します。



もちろん、コンピューターの場合、このタスクはやや複雑になります。 彼は人が持っている知識ベースを持っていません。 しかし、利用可能なデータを使用してコンピュータートレーニングをシミュレートしようとします。



データ



スクリプトをトレーニングするために、ロシア語の全国コーパスを使用しました。 コーパス部分SynTagRusは、品詞、数、格、動詞時制など、各単語のマークアップされた情報を含むテキストのコレクションです。 これは、XML形式の本文部分の外観です。



 <se> <w><ana lex="" gr="PR"></ana>`</w> <w><ana lex="" gr="S-PRO,n,sg=ins"></ana></w> <w><ana lex="" gr="S,m,anim=pl,nom"></ana>`</w> <w><ana lex="" gr="V,ipf,intr,act=pl,praes,3p,indic"></ana>`</w> <w><ana lex="" gr="PR"></ana></w> <w><ana lex="" gr="S,f,inan=pl,acc"></ana>`</w> . </se> <se> <w><ana lex="" gr="PART"></ana></w> <w><ana lex="" gr="ADV-PRO"></ana></w>, <w><ana lex="" gr="PR"></ana>`</w> <w><ana lex="" gr="NUM=acc"></ana></w> <w><ana lex="" gr="S,f,inan=pl,gen"></ana>`</w> <w><ana lex="" gr="PR"></ana></w> <w><ana lex="" gr="S,f,inan=pl,gen"></ana></w> , <w><ana lex="" gr="V,pf,intr,med=m,sg,praet,indic"></ana>``</w> <w><ana lex="" gr="A=m,sg,nom,plen"></ana>`</w> <w><ana lex="" gr="S,m,anim=sg,nom"></ana>`</w> . </se>
      
      







文は<se>タグで囲まれ、その中には<w>タグ内の単語があります。 各単語に関する情報は<ana>タグに含まれており、 lex属性はトークン(文法カテゴリ)に対応しています。 最初のカテゴリは品詞です:



'S': '.',

'A': '.',

'NUM': '.',

'A-NUM': '.-.',

'V': '.',

'ADV': '.',

'PRAEDIC': '',

'PARENTH': '',

'S-PRO': '. .',

'A-PRO': '. .',

'ADV-PRO': '. .',

'PRAEDIC-PRO': '. .',

'PR': '',

'CONJ': '',

'PART': '',

'INTJ': '.'








SVM



学習アルゴリズムとして、サポートベクトル法( SVM )を選択しました。 SVMまたは機械学習アルゴリズム全般に精通していない場合、SVMはデータ特性を入力として受け入れ、事前定義されたカテゴリに従って分類する一種のブラックボックスであると想像してください。 たとえば、単語の終わりを特性として指定し、品詞をカテゴリとして指定します。





ブラックボックスがスピーチの一部を自動的に認識するためには、まずトレーニングする必要があります。 入力例の多くの特徴と、音声出力の対応する部分を示します。 SVMは、ほとんどの場合、十分なデータで品詞を正しく決定するモデルを構築します。



アカデミックな目的であっても、SVMの実装は面倒なので、Pythonのラッパーを備えた既製のCIBライブラリLIBLINEARを使用します。 モデルをトレーニングするには、 train(prob、param)関数を使用します。この関数は、問題を最初の引数として取ります: problem(y、x) 、ここでyは配列xの各例の品詞の配列です。 各例は、特性のベクトルによって順番に表されます。 このような問題の声明を達成するには、最初に音声の各部分と各特性を特定の数値と相関させる必要があります。 例:



 '''  -   -   - . ''' x = [{1001: 1, 2001: 1, 3001: 1}, # 1001 - , 2001 - , 3001 -  {1002: 1, 2002: 1, 3001: 1}, # 1002 - , 2002 - , 3001 -  {1003: 1, 2003: 1, 3002: 1}] # 1003 - , 2003 - , 3002 -  y = [1, 1, 2] # 1 - , 2 - . import liblinearutil as svm problem = svm.problem(y, x) #   param = svm.parameter('-c 1 -s 4') #   model = svm.train(prob, param) #   #      '' label, acc, vals = svm.predict([0], {1001: 1, 2001: 1, 3001: 1}, model, '') # [0] - ,     
      
      







その結果、アルゴリズムは次のようになります。

  1. コーパスファイルを読み、各単語の特性を決定します。単語自体、語尾(最後の2文字と3文字)、接頭辞(最初の2文字と3文字)、および前の単語の音声部分
  2. 音声および特性の各部分にシリアル番号を割り当て、SVMをトレーニングするためのタスクを作成します
  3. SVMモデルの学習
  4. 訓練されたモデルを使用して、文中の単語のスピーチの部分を決定します:このため、各単語は再び特性の形式で提示され、SVMモデルの入力に供給され、最も適切なクラス、つまり選択されます。 品詞。




実装



ソースコードはここにあります: github.com/irokez/Pyrus/tree/master/src



本体


まず、マークされたケースを取得する必要があります。 ロシア語の国軍は非常に神秘的な方法で配布されています。 軍団のウェブサイト自体では、テキストのみを検索できますが、軍団全体をダウンロードすることはできません。
「コーパスのオフライン版は利用できませんが、コーパスからの無料の文の選択(混乱した順序で)は、18万語の使用法の同義語を削除しました(報道機関の90千、文学テキスト、法律および科学テキストからそれぞれ3万) 。
同時に、 それはウィキペディアに書かれています
「コーパスはオフラインで利用可能になり、非営利目的で配布されますが、現在はいくつかの技術的および/または著作権の問題のため、オンラインでのみアクセス可能です。」


私たちの目的のために、ケースの小さなサンプルが利用可能になりますが、こちらから入手できます: www.ruscorpora.ru/download/shuffled_rnc.zip



結果のアーカイブ内のファイルは、テキストをUTF-8に変換し 、XMLマークアップを修正するconvert-rnc.pyユーティリティを介して渡す必要があります。 その後、XMLを手動で修正する必要がある場合があります(xmllintが役立ちます)。 rnc.pyファイルには、正規化されたXML natファイルを読み取るための単純なReaderクラスが含まれています。 軍団。

 import xml.parsers.expat class Reader: def __init__(self): self._parser = xml.parsers.expat.ParserCreate() self._parser.StartElementHandler = self.start_element self._parser.EndElementHandler = self.end_element self._parser.CharacterDataHandler = self.char_data def start_element(self, name, attr): if name == 'ana': self._info = attr def end_element(self, name): if name == 'se': self._sentences.append(self._sentence) self._sentence = [] elif name == 'w': self._sentence.append((self._cdata, self._info)) elif name == 'ana': self._cdata = '' def char_data(self, content): self._cdata += content def read(self, filename): f = open(filename) content = f.read() f.close() self._sentences = [] self._sentence = [] self._cdata = '' self._info = '' self._parser.Parse(content) return self._sentences
      
      







Reader.read(self、filename)メソッドはファイルを読み取り、文章のリストを表示します:

 [[('`', {'lex': '', 'gr': 'S,m,anim=sg,nom'}), ('`', {'lex': '', 'gr': 'S,f,inan=sg,gen'}), ('`', {'lex': '', 'gr': 'A-PRO=f,sg,acc'}), ('`', {'lex': '', 'gr': 'S,m,anim=pl,nom'}), ('`', {'lex': '', 'gr': 'V,pf,tran=pl,act,praet,indic'}), ('', {'lex': '', 'gr': 'PR'}), ('', {'lex': '', 'gr': 'S,m,inan,0=sg,gen'}), ('`', {'lex': '', 'gr': 'V,pf,tran=m,sg,act,praet,indic'}), ('', {'lex': '', 'gr': 'S-PRO,pl,3p=dat'}), ('`', {'lex': '', 'gr': 'A=n,sg,acc,inan,plen'}), ('`', {'lex': '', 'gr': 'S,n,inan=sg,acc'}), ('', {'lex': '', 'gr': 'PR'}), ('', {'lex': '', 'gr': 'S-PRO,n,sg=acc'}), ('`', {'lex': '', 'gr': 'V,pf,intr,med=m,sg,praet,indic'}), ('`', {'lex': '', 'gr': 'S,f,inan=sg,ins'})]]
      
      







学習とマークアップ


SVMライブラリは、 http//www.csie.ntu.edu.tw/~cjlin/liblinear/からダウンロードできます 。 Pythonのラッパーを3番目のバージョンで動作させるために、小さなパッチを作成しました



pos.pyファイルには、 TaggerTaggerFeaturesという 2つの主要なクラスが含まれています。 Taggerは、実際には、テキストのマークアップを実行するクラスです。 各単語の品詞を定義します。 Tagger.trainメソッド(自己、文、ラベル)は、引数として文のリスト(rnc.Reader.readが生成するのと同じ形式)、および各単語の品詞のリストを取り、その後、ライブラリを使用してSVMモデルをトレーニングしますLIBLINEAR。 訓練されたモデルは、毎回モデルを訓練しないように(Tagger.saveメソッドを介して)その後保存されます。 Tagger.label(self、文)メソッドは、文をマークします。



TaggerFeaturesクラスは、トレーニングとレイアウトの特性を生成するように設計されています。 TaggerFeatures.from_body()は、単語の形式で特性を返します。 コーパス内の単語のIDを返します。 TaggerFeatures.from_suffix()およびTaggerFeatures.from_prefix()は、単語の末尾と接頭辞に特性を生成します。



モデルトレーニングを開始するために、 rnc.Readerを使用してシェルファイルを読み取り、Tagger.trainメソッドを呼び出すtrain.pyスクリプトが作成されました。

 import sys import re import rnc import pos sentences = [] sentences.extend(rnc.Reader().read('tmp/media1.xml')) sentences.extend(rnc.Reader().read('tmp/media2.xml')) sentences.extend(rnc.Reader().read('tmp/media3.xml')) re_pos = re.compile('([\w-]+)(?:[^\w-]|$)'.format('|'.join(pos.tagset))) tagger = pos.Tagger() sentence_labels = [] sentence_words = [] for sentence in sentences: labels = [] words = [] for word in sentence: gr = word[1]['gr'] m = re_pos.match(gr) if not m: print(gr, file = sys.stderr) pos = m.group(1) if pos == 'ANUM': pos = 'A-NUM' label = tagger.get_label_id(pos) if not label: print(gr, file = sys.stderr) labels.append(label) body = word[0].replace('`', '') words.append(body) sentence_labels.append(labels) sentence_words.append(words) tagger.train(sentence_words, sentence_labels, True) tagger.train(sentence_words, sentence_labels) tagger.save('tmp/svm.model', 'tmp/ids.pickle')
      
      







モデルが訓練されて保存された後、テキストをマークアップするためのスクリプトがようやく得られました。 使用例はtest.pyに示されています

 import sys import pos sentence = sys.argv[1].split(' ') tagger = pos.Tagger() tagger.load('tmp/svm.model', 'tmp/ids.pickle') rus = { 'S': '.', 'A': '.', 'NUM': '.', 'A-NUM': '.-.', 'V': '.', 'ADV': '.', 'PRAEDIC': '', 'PARENTH': '', 'S-PRO': '. .', 'A-PRO': '. .', 'ADV-PRO': '. .', 'PRAEDIC-PRO': '. .', 'PR': '', 'CONJ': '', 'PART': '', 'INTJ': '.', 'INIT': '', 'NONLEX': '' } tagged = [] for word, label in tagger.label(sentence): tagged.append((word, rus[tagger.get_label(label)])) print(tagged)
      
      







次のように機能します。

$ src/test.py " , "

[('', '.'), ('', '.'), ('', '. .'), ('', '.'), ('', '.'), (',', '.'), ('', ''), ('', '.'), ('', ''), ('', '.')]








テスト中



アルゴリズムの分類の精度を評価するために、トレーニングメソッドTagger.train()にはオプションのパラメーターcross_validationがあり 、これがTrueに設定されている場合、 クロスチェックを実行します。 トレーニングデータはK個の部分に分割されます。その後、各部分はメソッドの操作を評価するために順番に使用され、残りはトレーニングに使用されます。 NATのアクセス可能な部分のみが使用されていることを考えると、 92%の平均精度を達成することができました。 軍団。 通常、品詞のマークアップの精度は96〜98%です。



結論と将来の計画



一般的に、natと一緒に仕事をするのは面白かったです。 ケース。 多くの作業が行われていることがわかりますが、そこには私が十分に活用したい大量の情報が含まれています。 フルバージョンのリクエストを送信しましたが、残念ながらまだ回答がありません。



結果のマークアップスクリプトは簡単に拡張できるため、数、性別、ケースなど、他の形態学的カテゴリも定義できます。今後何をするか。 もちろん、将来、文構造を取得するためにロシア語の構文パーサーを作成したいと思いますが、これにはコーパスの完全版が必要です。



質問や提案にお答えできることを嬉しく思います。

ソースコードはこちらから入手できます: github.com/irokez/Pyrus

デモ: http : //vps11096.ovh.net : 8080



All Articles