自然言語処理100本ノック2020を解く(8章-前半)
あなたは何?
都市圏でITエンジニア(の研修)をやっているものです.
ありがたいことに,研修中にたくさんインプットができているので,少しずつ
アウトプットしていけたらと考えています.
本題
自然言語処理100本ノックの2020年版の解説があまり出回っていない気がしたので,需要
があるかと思い書いています.クオリティは担保しません.
今回は,8章の前半の解き方を載せます.合っているかはわかりません.
間違っていたらご指摘いただくと非常に非常に助かります.
70.単語ベクトルの和による特徴量
この章では,ニューラルネットによる学習をします.
入力が300次元の特徴ベクトル,出力が4次元のカテゴリ,というイメージです.
本問題では,入力の特徴ベクトルを作ります.特徴ベクトルの数式は以下.
は記事見出しに含まれる単語数で,はそれぞれの単語のベクトルです.
つまり,記事見出し中の単語ベクトルの平均を計算すれば良いです.
次に,正解ラベルについてですが,これはカテゴリ毎に何か番号を振れば良いです.
コード
import re import numpy from gensim.models import KeyedVectors news_path = '../data/GoogleNews-vectors-negative300.bin' words = KeyedVectors.load_word2vec_format(news_path, binary = True)#単語ベクトルの読み込み def vec2sum(x_data, dim): pattern = re.compile(r'[a-z|A-Z]+', re.MULTILINE + re.VERBOSE) vector_sum = np.zeros((len(vector_data), dim)) for i, s in enumerate(vector_data): vector = np.zeros(dim) cnt = 0 ss = pattern.findall(s) for noun in ss: try : cnt += 1 vector += words[noun] except KeyError as error: continue if cnt == 0: continue vector_sum[i] = (vector / cnt) return vector_sum dim = words['US'].shape[0] print(len(x_train)) x_train = vec2sum(x_train, dim) print(x_train.shape)
実行結果
8708 (8708, 300)
ベクトル辞書に含まれていない単語があるので,その場合は文字数をカウントせずにループを続けます.
とりあえず,記事数8708の300次元の特徴ベクトルができました.
71.単層ニューラルネットワークによる予測
次に,上記の特徴ベクトルとランダムな重み行列を掛け合わせ,それをソフトマックス関数の入力として計算します.
ソフトマックス関数は以下.
これをそのまま実装しても良いですが,今回はpytorchを用いて楽をします.
コード
import torch import torch.nn as nn import torch.nn.functional as func import torch.optim as optim from torch.autograd import Variable w = torch.randn(dim, 4, requires_grad = True) x_feature = torch.tensor(x_feature, requires_grad = True) # 単体事例 y = func.softmax(torch.mm(x_feature[:1].float(), w)) print(y) # 複数事例 Y = func.softmax(torch.mm(x_feature[:4].float(), w), dim = 1) print(Y)
実行結果
tensor([[0.0795, 0.1212, 0.1688, 0.6305]], grad_fn=<SoftmaxBackward>) tensor([[0.0645, 0.4789, 0.1243, 0.3324], [0.3788, 0.2161, 0.1569, 0.2482], [0.2830, 0.0544, 0.2407, 0.4219], [0.1088, 0.0486, 0.3351, 0.5075]], grad_fn=<SoftmaxBackward>)
72.損失と勾配の計算
クロスエントロピー誤差と重みに対する勾配を計算します.
クロスエントロピー誤差の算出は以下.tを正解ラベルとします.
これにsoftmaxの結果を放り込めばOK.集合に対してはその平均を返す.
ここもpytorchで楽をします.pytorchでは,CrossEntropyLoss
y_trainは正解ラベルです.
コード
from math import exp, log criterion = nn.CrossEntropyLoss() y_train = torch.tensor(y_train.astype(float), requires_grad = True) # 単体事例 y_ = criterion(torch.mm(x_feature[:1].float(), w), y_train[:1].long()) print(y_) y_.backward() print(w.grad) # 複数事例 Y_ = criterion(torch.mm(x_feature[:4].float(), w), y_train[:4].long()) print(Y_) Y_.backward() print(w.grad) # 検算 ans = [] for yy, i in zip(Y, y_train[:4].long()): ans.append(-log(yy[i])) print(sum(ans) / len(ans))
実行結果
tensor(2.0852, grad_fn=<NllLossBackward>) tensor([[-0.0011, -0.0080, 0.0147, -0.0056], [ 0.0072, 0.0533, -0.0976, 0.0370], [-0.0008, -0.0062, 0.0113, -0.0043], ..., [-0.0055, -0.0412, 0.0753, -0.0286], [-0.0021, -0.0154, 0.0282, -0.0107], [-0.0010, -0.0071, 0.0129, -0.0049]]) tensor(1.2895, grad_fn=<NllLossBackward>) tensor([[-9.5167e-03, -1.6568e-03, 4.1909e-03, 6.9826e-03], [-5.0837e-03, 7.4656e-02, -1.3673e-01, 6.7157e-02], [ 7.1513e-03, -1.1068e-02, 1.4147e-02, -1.0230e-02], ..., [-9.3671e-03, -4.7332e-02, 9.5406e-02, -3.8707e-02], [ 9.5714e-05, -1.8119e-02, 3.1322e-02, -1.3299e-02], [ 7.7742e-03, -1.2784e-02, 3.0687e-02, -2.5677e-02]]) 1.2895281521241457
出ましたね.検算を一応してみましたが,合っていました.
勾配に関しては,backward()で微分をしてくれるので,とても楽ができます.
今回は一旦ここまでとします.後半はひとまとまりにできるので...
では...!
出典:
自然言語処理100本ノック
nlp100.github.io