見出し画像

自然言語処理②~n-gramとか実装していく~

前回は導入のみで外観を話してきましたが、今回から実際にコードを書いていきます。

・まず初めに環境について。

皆さんの前回までのコードを書いていた環境はわかりませんが、自然言語処理に関しては(あくまで自分は)Google colaboratoryを使います。

正直MeCabとかのセットアップがだるいのもありますが、自然言語処理のデータは大きいことが多く、ローカルにダウンロードするのが嫌ということから、ubuntu(たしか?)コマンドが使えるcolaboratoryが手っ取り早いです。

googleアカウント持ってればすぐに使えるので、気軽に使っていただけます。(ま、ランタイムきれたら面倒でもあるので、jupyterでもお好きな方でよきかと)

・n-gram

ほんとの初めの初めから。

まずは単語をn個単位で初めから区切っていけばなんかいいんじゃね?というのがn-gramです。

実は単語ごとに区切るということは自然言語処理においても避けて通れないですし、sklearnでもn-gramを使うことができます。

非常に簡易的ですが、初めの一歩として欠かせないステップかなと思います

(後ほど100本ノックの方でコードを扱います。)

少し2-gram(bi-gram)を見てみます。

I think NLP is getting popular

を2単語ずつに区切ってその全種類をリストなどで出します。

'I think', 'think NLP', 'NLP is', 'is getting', 'getting popular'

が2-gramで出てくる結果となります。

英語は単語の間に半角スペースが入ってくるので、作業としてもこれほどないくらいわかりやすいですが、日本語は本当にややこしいです。

「私は今日浅草に行きました」

であれば、

'私 は', 'は 今日', '今日浅草', ・・・

ってな感じです。これをコードで書くとなると日本語の単語ごとに区切る操作が一つ乗っかるので、本当にややこしいです(2回目)

・100本ノックを使って実装の練習

100本ノックにて0-5はn-gramの問題なので、練習がてら実装を。

回答は転がってますが、自分なりの回答を書いたので、参考までに

ちなみに、2単語、2文字とあるのですが、2文字において、必ず挿入される半角スペースも一文字にカウントすることは日本人にとって馴染みないので、書いて欲しいもんですw

def n_gram(n, text):
 """
 make the n-gram
 parameter:
   n: int, group by n words
   text: the text or str you want to use n-gram 
 """

 # word
 split_text = text.split()
 counts_of_words = len(split_text)

 n_list = []
 for i in range(counts_of_words - n + 1):
   n_words = ' '.join(split_text[i:i+n])
   n_list.append(n_words)

 # letter
 text = ' '.join(split_text)
 letter_list = [letter for letter in text]

 counts_of_letters = len(letter_list)

 n_letter = []
 for i in range(counts_of_letters - n + 1):
   n_letters = ''.join(letter_list[i:i+n])
   n_letter.append(n_letters)

 return n_list, n_letter

回答にある出力結果とは違い明日が、意味的には同じなので個人的にオッケーとしています。

text = 'I am an NLPer'
bi_gram = n_gram(2, text)
bi_gram

スクリーンショット 2021-08-25 21.26.13

2単語、2文字とあったので、関数で両方出すことにしています。回答例の方が賢いとは思うので、特に上記のコードを真似る必要はないですw。

・第1章で気になった問題

せっかくなので、第1章の他の問題で気になったものがいくつかあるので、自分のダサいコードと、模範回答を載せてみていきましょう。

01. 「パタトクカシーー」
「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ

- 自分のコード

patocar = 'パタトクカシーー'
answer = patocar[0] + patocar[2] + patocar[4] + patocar[6]
answer

正しい回答はできますが、めちゃくちゃザコいです。。。

- 回答例

str = 'パタトクカシーー'
ans = str[::2]  # 「先頭」から「末尾」まで「移動幅2」で

print(ans)

str[::n]で幅nで文字列を取得してくれます。(こういうの、なかなか出会う機会がない。。)

08. 暗号文
与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
英小文字ならば(219 - 文字コード)の文字に置換 その他の文字はそのまま出力 この関数を用い,英語のメッセージを暗号化・復号化せよ.
def cipher(word):
 letters = []
 for letter in word:
   if letter.islower():
     # ord(str)で文字コードを取得
     code = 219 - int(ord(letter))
     # chr(code)でcodeの文字に変換
     char = chr(code)
     letter = char

   letters.append(letter)

 return ''.join(letters)

間違いではないのですが、まぁ、ださいです。。

普通にリスト内包表記でワンライナーでした。。

- 回答例

def cipher(str):
 rep = [chr(219 - ord(x)) if x.islower() else x for x in str]

 return ''.join(rep)

message = 'the quick brown fox jumps over the lazy dog'
message = cipher(message)
print('暗号化:', message)
message = cipher(message)
print('復号化:', message)

回答例は全て先程の参考URLから参照しています。(本当に助かりますね!)

・終わり

次回以降でようやくMeCabとか使おうと思います。

また、100本ノックも気になった問題は適宜載せていこうと思います。

この記事が気に入ったらサポートをしてみませんか?