<jupyter_start><jupyter_text>PyTorch tutorial の内容について(2)- date: 2020-0722- author: 浅川伸一[https://github.com/pytorch/tutorials/tree/master/beginner_source/nlp](https://github.com/pytorch/tutorials/tree/master/beginner_source/nlp) を見るとPyTorch で 自然言語処理を行う場合のチュートリアルは以下とおりである Deep Learning for NLP with Pytorch1. [pytorch_tutorial.py](https://github.com/pytorch/tutorials/blob/master/beginner_source/nlp/pytorch_tutorial.py): 	[PyTorch 入門 Introduction to PyTorch](https://pytorch.org/tutorials/beginner/nlp/pytorch_tutorial.html)2. [deep_learning_tutorial.py](https://github.com/pytorch/tutorials/blob/master/beginner_source/nlp/deep_learning_tutorial.py): 	[PyTorch による深層学習 Deep Learning with PyTorch](https://pytorch.org/tutorials/beginner/nlp/deep_learning_tutorial.html)3. [word_embeddings_tutorial.py](https://github.com/pytorch/tutorials/blob/master/beginner_source/nlp/word_embeddings_tutorial.py): 	[単語埋め込み:語彙的意味の符号化 Word Embeddings: Encoding Lexical Semantics](https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html)4. [sequence_models_tutorial.py]((https://github.com/pytorch/tutorials/blob/master/beginner_source/nlp/sequence_models_tutorial.py): 	[系列モデルと LSTM Sequence Models and Long-Short Term Memory Networks](https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html)5. [advanced_tutorial.py]((https://github.com/pytorch/tutorials/blob/master/beginner_source/nlp/advanced_tutorial.py): 	[動的意思決定と双方向 LSTM 条件付き確率場 Advanced: Making Dynamic Decisions and the Bi-LSTM CRF](https://pytorch.org/tutorials/beginner/nlp/advanced_tutorial.html)以下では，このうちの 2 について解説している。<jupyter_code>%matplotlib inline<jupyter_output><empty_output><jupyter_text>PyTorch によるディープラーニング******************************** ディープラーニングのブルディンクブロック: アフィン変換，非線形性，目的関数ディープラーニングは、線形性と非線形性を巧妙な方法で組み合わせることで構成されています。非線形性を導入することで、強力なモデルを作ることができます。このセクションでは、これらのコアコンポーネントで遊び、目的関数を構成し、モデルがどのように訓練されるかを見ていきます。<!--Deep learning consists of composing linearities with non-linearities in clever ways. The introduction of non-linearities allows for powerful models. In this section, we will play with these core components, make up an objective function, and see how the model is trained.--> アフィン写像 ディープラーニングの中核を成すものの一つがアフィン写像であり、次式の関数 $f(x)$ で表します。\begin{align}f(x) = Ax + b\end{align}行列 $A$ とベクトル $x, b$ を求めます。ここで学習するパラメータは $A$ と $b$ です。$b$ はバイアスとして使用されます。PyTorch や他のほとんどのディープラーニングフレームワークでは 従来の線形代数とは少し違うことをしています。それは、入力の列の代わりに 行を写像します。つまり 以下の出力の $i$'番目の行は $A$ の下の入力の $i$'th 行にバイアス項を加えたものです。下の例を見てください。<!--for a matrix $A$ and vectors $x, b$. The parameters to be learned here are $A$ and $b$. Often, $b$ is refered toas the *bias* term.PyTorch and most other deep learning frameworks do things a little differently than traditional linear algebra. It maps the rows of the input instead of the columns. That is, the $i$'th row of the output below is the mapping of the $i$'th row of the input under $A$, plus the bias term. Look at the example below.--><jupyter_code># Author: <NAME>

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)
lin = nn.Linear(5, 3)  # 5 次のベクトルから 3 次のベクトルへの線形写像 ``nn.Linear`` #  maps from R^5 to R^3, parameters A, b
# データは 2 行 5 列 すなわち 5 次元のベクトルからなるデータが 2 つ # data is 2x5.  A maps from 5 to 3... can we map "data" under A?
data = torch.randn(2, 5)
print(lin(data))  # 実際には 行列積<jupyter_output>tensor([[ 0.1755, -0.3268, -0.5069],
        [-0.6602,  0.2260,  0.1089]], grad_fn=<AddmmBackward>)<jupyter_text>非線形性 次の事実に注意してください。2 つのアフィン写像 $f(x) = Ax + b$ と $g(x) = Cx + d$ があるとします。$f(g(x))$ は何でしょうか？<!--First, note the following fact, which will explain why we need non-linearities in the first place. Suppose we have two affine maps $f(x) = Ax + b$ and $g(x) = Cx + d$. What is $f(g(x))$?-->\begin{align}f(g(x)) = A(Cx + d) + b = ACx + (Ad + b)\end{align}$A$ と $C$ は行列であり，$Ad + b$ はベクトルです。アフィン写像の合成はアフィン写像であることがわかります。<!--From this, you can see that if you wanted your neural network to be long chains of affine compositions, that this adds no new power to your model than just doing a single affine map.-->このことから，ニューラルネットワークをアフィン写像の幾重にも重ねた，ディープニューラルネットワークモデルを作成する場合，単一のアフィン写像による成績以上の性能は得られないことを意味します。<!--If we introduce non-linearities in between the affine layers, this is no longer the case, and we can build much more powerful models.-->逆に，アフィン写像の間に非線形性を導入するとにより，より強力なモデルを構築することができます。いくつかのコアとなる非線形性があります。$\tanh(x)$, $\sigma(x)$ (シグモイド関数), $\text{ReLU}(x)$ が一般的です。他にも数多非線形変換がありうるのに，なぜこれらが用いられるのか疑問に思うかも知れません。理由は勾配計算が容易だからです。勾配の計算はニューラルネットワークの学習には必須です。<!--$\tanh(x), \sigma(x), \text{ReLU}(x)$ are the most common. You are probably wondering: "why these functions? I can think of plenty of other non-linearities." The reason for this is that they have gradients that are easy to compute, and computing gradients is essential for learning.-->例えば\begin{align}\frac{d\sigma}{dx} = \sigma(x)(1 - \sigma(x))\end{align}<!--A quick note: although you may have learned some neural networks in your intro to AI class where $\sigma(x)$ was the default non-linearity, typically people shy away from it in practice. This is because the gradient *vanishes* very quickly as the absolute value of the argument grows. Small gradients means it is hard to learn. Most people default to tanh or ReLU.-->昔からニューラルネットワークをご存知の方は，シグモイド関数 $\sigma(x)$ がデフォルトであると思われていることでしょう。ですが，最近ではシグモイド関数は用いられません。理由は勾配消失問題に対処するのが難しいからです。このため，現在では $\tanh(x)$ や ReLU (整流線型ユニット) が用いられます。<jupyter_code># pytorch では ほとんどの非線形活性化関数は torch.functional で定義されています (F としてインポートしています)
# 非線形型は一般的にアフィン写像のような パラメータを持たちません。
# すなわち 学習中に更新されるパラメータを持ちません。
data = torch.randn(2, 2)
print(data)
print(F.relu(data))<jupyter_output>tensor([[-0.5404, -2.2102],
        [ 2.1130, -0.0040]])
tensor([[0.0000, 0.0000],
        [2.1130, 0.0000]])<jupyter_text>ソフトマックスと確率<!--The function $\text{Softmax}(x)$ is also just a non-linearity, but it is special in that it usually is the last operation done in a network. This is because it takes in a vector of real numbers and returns a probability distribution. Its definition is as follows. Let $x$ be a vector of real numbers (positive, negative, whatever, there are no constraints). Then the i'th component of $\text{Softmax}(x)$ is-->関数 $\text{Softmax}(x)$ も単なる非線形活性化関数の一つです。ですが，通常ネットワーク内で最終層で行われる操作であるという点で特殊です。ソフトマックス関数 は実数のベクトルを引数として取り，各ニューロン (ユニット) の確率分布を返すからです。ソフトマックス関数の定義は次のようになります。$x$ を実ベクトルとする (正でも負でも何でもよく，制約はない)。$text{Softmax}(x)$ の i 番目の成分は以下になります:\begin{align}\frac{\exp(x_i)}{\sum_j \exp(x_j)}\end{align}<!--It should be clear that the output is a probability distribution: each element is non-negative and the sum over all components is 1.You could also think of it as just applying an element-wise exponentiation operator to the input to make everything non-negative and then dividing by the normalization constant.-->出力値が確率分布をなす (各要素は非負で，かつ，全要素の総和が 1 ) ことは，明らかに重要です。入力に要素ごとの 指数化演算子を適用してすべてを非負にして，、正規化定数で除算すると考えることもできます。--- 訳注: Python で頻用される科学計算ライブラリ `scipy.special` には `scipy.special.logsumexp()` という関数が用意されている。上記ソフトマックス関数を計算する際に，分母 $\sum\exp(x_j)$ は 指数変換した各値を総和する (`sumexp` する) ことを意味し，確率密度関数の対数尤度を計算する場合には，この `sumexp` を対数変換するので `logsumexp` と命名された。`logsumexp()` 関数を用いてソフトマックス関数を定義すれば以下のようになる:\begin{align}\log\text{softmax}(x_i) = x_i - \text{logsumexp(x)}\end{align}すなわちソフトマックス関数の対数を計算するときには，各値から `logsumexp(x)` を減じるだけになるので，計算が極端にかんたんになる。筆者の知る限り，Fortran 時代から logsumexp ルーチンは存在する。もはやその起源や命名者を特定できないように見受けられる。実際，以下のコードは同じ値を与える。```pythonimport numpy as npfrom scipy.special import softmax, logsumexpx1, x2, x3 = 1., 2., 3. print(np.log(softmax([x1, x2, x3]))[0])print( x1 - logsumexp([x1, x2, x3]))```<jupyter_code># ソフトマックス関数 Softmax は torch.nn.functional でも定義されている
data = torch.randn(5)
print(data)
print(F.softmax(data, dim=0))
print(F.softmax(data, dim=0).sum())  # 確率分布であるから総和は 1 となる
print(F.log_softmax(data, dim=0))  # ソフトマックス関数の対数変換<jupyter_output>tensor([ 1.3800, -1.3505,  0.3455,  0.5046,  1.8213])
tensor([0.2948, 0.0192, 0.1048, 0.1228, 0.4584])
tensor(1.)
tensor([-1.2214, -3.9519, -2.2560, -2.0969, -0.7801])<jupyter_text>目的関数目的関数は学習に用いられ，ネットワークがその値を最小化されるよう訓練される関数です(*損失関数* または *コスト関数* とも呼ばれます)。これは，最初に 訓練インスタンス を選択し，ニューラルネットワークを実行して 出力関数の損失値を計算することで進行します。その後，モデルのパラメータは，損失関数の微分を取ることで更新されます。直感的には，モデルがその答えに完全に自信を持っていて，その答えが誤っている場合，損失関数の値は大きくなります。反対に，その答えに非常に自信を持っていて，かつ，その答えが正しければ、損失値は小さくなります。学習事例の損失関数を最小化するという考え方は，ネットワークの一般化に関与します。訓練データセット，とテストデータセット，を分割するのは，訓練環境での未見のデータでの損失が小さいことを期待するためです。損失関数の例としては、*負の対数尤度損失* があります。これは多クラス分類の非常に一般的な目的関数です。教師付き多クラス分類では，正しい出力の負の対数確率を最小にするようにネットワークを訓練することを意味します (正しい出力に対する対数確率を最大にする)。(訳注: 目的関数 ojbective function は，損失関数 loss function は，ほぼ同義で使われます。Goodfellow の教科書では objective, loss, goal, error function などは同義としてで扱う書かれています。)<!---The objective function is the function that your network is being trained to minimize (in which case it is often called a *loss function* or *cost function*). This proceeds by first choosing a training instance, running it through your neural network, and then computing the loss of the output. The parameters of the model are then updated by taking the derivative of the loss function. Intuitively, if your model is completely confident in its answer, and its answer is wrong, your loss will be high. If it is very confident in its answer, and its answer is correct, the loss will be low.The idea behind minimizing the loss function on your training examples is that your network will hopefully generalize well and have small loss on unseen examples in your dev set, test set, or in production. An example loss function is the *negative log likelihood loss*, which is a very common objective for multi-class classification. For supervised multi-class classification, this means training the network to minimize the negative log probability of the correct output (or equivalently, maximize the log probability of the correct output).-->  最適化と訓練So what we can compute a loss function for an instance? What do we do with that? We saw earlier that Tensors know how to compute gradients with respect to the things that were used to compute it. Well, since our loss is an Tensor, we can compute gradients with respect to all of the parameters used to compute it! Then we can perform standard gradient updates. Let $\theta$ be our parameters, $L(\theta)$ the loss function, and $\eta$ a positive learning rate. Then:ある事例 (データ) の損失関数を実際どう計算するのでしょうか？先ほど，PyTorch のテンソルは計算に使われたすべてについての勾配計算に関する情報を知っていると説明しました。損失関数は PyTorch テンソル なので，計算に使われたすべてのパラメータに関する勾配を計算することができます。標準的な勾配降下法によりパラメータ更新が可能です。$\theta$ をパラメータとし，損失関数を $L(\theta)$，正の学習率を $\eta$ とすれば次式を得ます:\begin{align}\theta^{(t+1)} = \theta^{(t)} - \eta \nabla_\theta L(\theta)\end{align}<!--There are a huge collection of algorithms and active research in attempting to do something more than just this vanilla gradient update.Many attempt to vary the learning rate based on what is happening at train time. You don't need to worry about what specifically these algorithms are doing unless you are really interested. Torch provides many in the torch.optim package, and they are all completely transparent. Using the simplest gradient update is the same as the more complicated algorithms. Trying different update algorithms and different parameters for the update algorithms (like different initial learning rates) is important in optimizing your network's performance. Often, just replacing vanilla SGD with an optimizer like Adam or RMSProp will boost performance noticably.-->このバニラ勾配降下法 による更新だけでなく，それ以上のことをしようとする試みには，膨大なアルゴリズムの研究が集積されていてり，活発な研究が行われています。(訳注: 何の飾り付けのない素のアルゴリズムという意味でしばしば `バニラ` vanilla という形容詞を用います。何のフレーバーもつかない素のアイスクリームをバニラアイスクリームと呼ぶように，例えば，教科書に記載されているような素の誤差逆伝播法のことを `バニラバックプロパゲーション` などと呼ぶ習慣があります)バニラではない，多くの発展的なアルゴリズムでは，訓練時に起こっていることに基づいて学習率を変化させます。本当に興味がない限り，これらのアルゴリズムが具体的に何をしているのかを気にする必要はありません。Torch では `torch.optim` パッケージで多くのアルゴリズムを提供しています。それらアルゴリズムはすべて完全に透過的です。すなわち，最も単純な勾配更新法を使うこととは，より複雑なアルゴリズムと同じく扱うことができます。ネットワークのパフォーマンスを最適化するには，さまざまな更新アルゴリズムや更新アルゴリズムのパラメータ (初期学習率の違いなど) を試すことが重要です。多くの場合， `バニラ SGD` を `Adam` や `RMSProp` のような最適化手法に置き換えるだけで，パフォーマンスが顕著に向上します。（訳注: バニラ SGD ではハイパーパラメータ $\eta$ が固定です。一方 `RMSprop` 以降のアルゴリズムでは，各パラメータごとに異なる $\eta$ を採用します。また，2 次微分 **ヘッセ行列** を用いるニュートン法の近似として，ヘッセ行列を近似式で置き換えた準ニュートン法のニューラルネットワーク的実装が `Adam` です。ニュートン法からの類推で明らかなようにAdam では学習係数 $\eta$ をヘシアンに応じて調整します。）  PyTorch によるネットワークコンポネントの作成<!--Before we move on to our focus on NLP, lets do an annotated example of building a network in PyTorch using only affine maps and non-linearities. We will also see how to compute a loss function, using PyTorch's built in negative log likelihood, and update parameters by backpropagation.All network components should inherit from nn.Module and override the forward() method. That is about it, as far as the boilerplate is concerned. Inheriting from nn.Module provides functionality to your component. For example, it makes it keep track of its trainable parameters, you can swap it between CPU and GPU with the ``.to(device)`` method, where device can be a CPU device ``torch.device("cpu")`` or CUDA device ``torch.device("cuda:0")``. Let's write an annotated example of a network that takes in a sparse bag-of-words representation and outputs a probability distribution over two labels: "English" and "Spanish". This model is just logistic regression.-->NLP の話に移る前に，アフィン写像と非線形性だけを使って PyTorch でネットワークを構築してみましょう。加えて，PyTorch の負の対数尤度を使って損失関数を計算し，誤差逆伝播法 (バックプロパゲーション) でパラメータを更新する方法も見てみましょう。すべてのネットワークコンポーネントは `nn.Module` を継承し，`forward()` メソッドをオーバーライドする必要があります。ボイラープレート (訳注: いわゆるテンプレート版あるいはプロトタイプ，オブジェクト指向でのクラス定義的な意味合いです) に関する限りでは、これくらいです。`nn.Module` を継承することで コンポーネントに関数 (機能) を提供します。例えば 訓練可能なパラメータを管理したり  ``.to(device)`` メソッドを使って CPU と GPU を入れ替えたりすることができます。疎な bag-of-words 表現を受け取り，2 つのラベルの確率分布を出力するネットワークの注釈付きの例を書いてみましょう。「英語」と「スペイン語」の 2 つのラベルの確率を出力するネットワークの例を書いてみましょう。このモデルは単なるロジスティック回帰です。  例: Bog-of-Words によるロジスティック回帰分類器<!-- Example: Logistic Regression Bag-of-Words classifier--><!--Our model will map a sparse BoW representation to log probabilities over labels. We assign each word in the vocab an index. For example, say our entire vocab is two words "hello" and "world", with indices 0 and 1 respectively. The BoW vector for the sentence "hello hello hello hello" is-->モデルは 疎な BoW 表現をラベルの対数確率にマッピングするものです。語彙内の各単語にインデックスを割り当てます。例えば，全語彙が "hello" と "world" の 2 つの単語で成り立っているとします。それぞれ 0 と 1 のインデックスを持つとします。文 "hello hello hello hello hello hello " の BoW ベクトルは次のようになります。\begin{align}\left[ 4, 0 \right]\end{align}同様に，"hello, workd world hello" の BoW ベクトルは次のようになります:\begin{align}\left[ 2, 2 \right]\end{align}一般には，次のような表記となります:\begin{align}\left[ \text{Count}(\text{hello}), \text{Count}(\text{world}) \right]\end{align}この BOW ベクトルを $x$ と表記します。ネットワークの出力は以下のとおり:\begin{align}\log \text{Softmax}(Ax + b)\end{align}すなわち入力信号を アフィン写像に通して，その値に対して log softmax を行います。<jupyter_code>data = [("me gusta comer en la cafeteria".split(), "SPANISH"),
        ("Give it to me".split(), "ENGLISH"),
        ("No creo que sea una buena idea".split(), "SPANISH"),
        ("No it is not a good idea to get lost at sea".split(), "ENGLISH")]

test_data = [("Yo creo que si".split(), "SPANISH"),
             ("it is lost on me".split(), "ENGLISH")]

# word_to_ix は語彙中の各単語をとユニークな整数に変換します。
word_to_ix = {}
for sent, _ in data + test_data:
    for word in sent:
        if word not in word_to_ix:
            word_to_ix[word] = len(word_to_ix)
print(word_to_ix)

VOCAB_SIZE = len(word_to_ix)
NUM_LABELS = 2


class BoWClassifier(nn.Module):  # inheriting from nn.Module!

    def __init__(self, num_labels, vocab_size):
        # nn.Module の init 関数を呼び出します。文法で混乱しないように。
        # nn.Module での初期化が行われます
        super(BoWClassifier, self).__init__()

        # 必要なパラメータの定義。アフィン写像のパラメータ A と b 
        # Torch には nn.Linear() が定義されており，これがアフィン写像を提供します
        # 入力次元が vocab_size であり，出力が num_labels である理由を理解してください。
        self.linear = nn.Linear(vocab_size, num_labels)

        # 非線形関数 log softmax にはパラメータがないことに注意

    def forward(self, bow_vec):
        # 入力を線形層に通し，さらに log_softmax に通します。
        # 多くの非線形関数ややその他の関数は torch.nn.functional にあります
        return F.log_softmax(self.linear(bow_vec), dim=1)


def make_bow_vector(sentence, word_to_ix):
    vec = torch.zeros(len(word_to_ix))
    for word in sentence:
        vec[word_to_ix[word]] += 1
    return vec.view(1, -1)


def make_target(label, label_to_ix):
    return torch.LongTensor([label_to_ix[label]])


model = BoWClassifier(NUM_LABELS, VOCAB_SIZE)

# モデルはパラメータを知っています。 以下の最初の出力は A，2番目の出力は bです。
# モジュールの __init__ 関数でクラス変数にコンポーネントを代入するときはいつも
# self.linear = nn.Linear(...) 行で行われていました。
# PyTorch 開発者による Python 魔法により，このモジュール (BoWClassifier) は
# nn.Linear のパラメータの知識を保持しています
for param in model.parameters():
    print(param)

# このモデルを走らせるためには，BoW ベクトルを通します
# ここでは訓練する必要がないので，以下では torch.no_grad() で warp します
with torch.no_grad():
    sample = data[0]
    bow_vector = make_bow_vector(sample[0], word_to_ix)
    log_probs = model(bow_vector)
    print(log_probs)<jupyter_output>{'me': 0, 'gusta': 1, 'comer': 2, 'en': 3, 'la': 4, 'cafeteria': 5, 'Give': 6, 'it': 7, 'to': 8, 'No': 9, 'creo': 10, 'que': 11, 'sea': 12, 'una': 13, 'buena': 14, 'idea': 15, 'is': 16, 'not': 17, 'a': 18, 'good': 19, 'get': 20, 'lost': 21, 'at': 22, 'Yo': 23, 'si': 24, 'on': 25}
Parameter containing:
tensor([[ 0.1194,  0.0609, -0.1268,  0.1274,  0.1191,  0.1739, -0.1099, -0.0323,
         -0.0038,  0.0286, -0.1488, -0.1392,  0.1067, -0.0460,  0.0958,  0.0112,
          0.0644,  0.0431,  0.0713,  0.0972, -0.1816,  0.0987, -0.1379, -0.1480,
          0.0119, -0.0334],
        [ 0.1152, -0.1136, -0.1743,  0.1427, -0.0291,  0.1103,  0.0630, -0.1471,
          0.0394,  0.0471, -0.1313, -0.0931,  0.0669,  0.0351, -0.0834, -0.0594,
          0.1796, -0.0363,  0.1106,  0.0849, -0.1268, -0.1668,  0.1882,  0.0102,
          0.1344,  0.0406]], requires_grad=True)
Parameter containing:
tensor([0.0631, 0.1465], requires_grad=True)
tensor([[-0.5378, -0.8771]])<jupyter_text>上の値のうち、どれが ENGLISH の対数確率に対応し，どれが SPANISH の対数確率に対応するのでしょうか？これまで定義したことはありませんでしたが，もし学習したいのであれば定義する必要があります。<jupyter_code>label_to_ix = {"SPANISH": 0, "ENGLISH": 1}<jupyter_output><empty_output><jupyter_text><!--So lets train! To do this, we pass instances through to get log probabilities, compute a loss function, compute the gradient of the loss function, and then update the parameters with a gradient step. Loss functions are provided by Torch in the nn package. nn.NLLLoss() is the negative log likelihood loss we want. It also defines optimization functions in torch.optim. Here, we will just use SGD.Note that the *input* to NLL Loss is a vector of log probabilities, and a target label. It doesn't compute the log probabilities for us. This is why the last layer of our network is log softmax. The loss function nn.CrossEntropyLoss() is the same as NLLLoss(), except it does the log softmax for you.-->以下は訓練です。訓練には，インスタンスを通過させて対数確率を取得し，損失関数を計算し，損失関数の勾配を計算し，勾配ステップでパラメータを更新します。損失関数は Torch の nn パッケージ内で提供されています。`nn.NLLLoss()` は求める負の対数尤度損失です。  (訳注: NLL とは Negative Log Loss 関数，負の対数尤度を意味します)また 最適化関数も torch.optim で定義されています。ここでは SGD を使用します。NLL 損失への *input* は対数確率のベクトルとターゲットラベルであることに注意してください。この関数は 対数確率を計算してくれません。これが，このネットワークの最後の層が log softmax である理由です。損失関数 nn.CrossEntropyLoss() は NLLLoss() と同じですが、log softmax を計算してくれます。<jupyter_code># 訓練前にテストデータを評価します。訓練前後での変化を見るためです
with torch.no_grad():
    for instance, label in test_data:
        bow_vec = make_bow_vector(instance, word_to_ix)
        log_probs = model(bow_vec)
        print(log_probs)

# "creo" に対応する行列の列を印字
print(next(model.parameters())[:, word_to_ix["creo"]])

loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)

# 学習データを何度か通したいと思うでしょう.
# 100 は実際のデータセットよりもはるかに大きいです。
# ですが 実際のデータセットは 2 つ以上のインスタンスを持っています。
# 通常 5 から 30 エポックの間のどこかが妥当です。
for epoch in range(100):
    for instance, label in data:
        # ステップ 1. PyTorch には勾配が蓄積されていることを覚えておいてください
        # 各インスタンスの前にその勾配を ゼロ消去する必要があります
        model.zero_grad()

        # Step 2. Make our BOW vector and also we must wrap the target in a
        # Tensor as an integer. For example, if the target is SPANISH, then
        # we wrap the integer 0. The loss function then knows that the 0th
        # element of the log probabilities is the log probability
        # corresponding to SPANISH
        
        bow_vec = make_bow_vector(instance, word_to_ix)
        target = make_target(label, label_to_ix)

        # Step 3. Run our forward pass.
        log_probs = model(bow_vec)

        # Step 4. Compute the loss, gradients, and update the parameters by
        # calling optimizer.step()
        loss = loss_function(log_probs, target)
        loss.backward()
        optimizer.step()

with torch.no_grad():
    for instance, label in test_data:
        bow_vec = make_bow_vector(instance, word_to_ix)
        log_probs = model(bow_vec)
        print(log_probs)

# Index corresponding to Spanish goes up, English goes down!
print(next(model.parameters())[:, word_to_ix["creo"]])<jupyter_output>tensor([[-0.9297, -0.5020]])
tensor([[-0.6388, -0.7506]])
tensor([-0.1488, -0.1313], grad_fn=<SelectBackward>)
tensor([[-0.2093, -1.6669]])
tensor([[-2.5330, -0.0828]])
tensor([ 0.2803, -0.5605], grad_fn=<SelectBackward>)
{'fraction_non_alphanumeric': 0.10943951402401404, 'fraction_numerical': 0.029756537421100092, 'mean_word_length': 5.26397146254459, 'pattern_counts': {'":': 2, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '270883', 'license': 'MIT', 'n_tokens_mistral': 10938, 'n_tokens_neox': 10184, 'n_words': 2094, 'path': 'notebooks/02deep_learning_tutorial.ipynb', 'repo_name': 'JPA-BERT/jpa-bert.github.io', 'size': '21079'}