---
type: slides
---

# モデルのトレーニングと更新

Notes: この最終章では、最新の NLP の素晴らしい側面の 1 つであるモデルのトレーニ
ングについて紹介します！このレッスンでは固有表現抽出器にフォーカスし、spaCy のニ
ューラルネットワークモデルのトレーニングと必要なデータについて学んでいきます。

---

# なぜモデルをアップデートするか？

- 特定のドメインでより良い結果が得られます
- 問題に特化した分類スキームを学習できます
- テキストの分類に必須です
- 固有表現抽出に非常に便利です
- 品詞タグ付けや依存性解析については重要性が低いです

Notes: やり方の説明を始める前に、自問自答してみましょう。なぜ私たちは自分のデー
タでモデルを更新したいのでしょうか？なぜ事前に訓練されたモデルを使うだけではダメ
なのでしょうか？

統計モデルは、訓練された例に基づいて予測を行います。

通常、適用するドメインの例を示すことで、モデルの予測をより正確にできます。

また、問題に固有のカテゴリを予測したい場合がよくあり、モデルはその新しいカテゴリ
について学習する必要があります。

モデルのアップデートはテキスト分類には必須で、固有表現抽出には非常に有用ですが、
タグ付けや構文解析にはそれほど重要ではありません。

---

# トレーニングはどのように行われるか(1)

1. モデルの重みをランダムに**初期化**します
2. 現在の重みでいくつかの例を**予測**します
3. 予測値と真のラベルを**比較**します
4. 予測を改善するため、重みの変更方法を**計算**します
5. 重みを少し**更新**します
6. 2 に戻ります

Notes: spaCy は、新たなデータによる既存のモデル更新や、新しいモデルの学習をサポ
ートしています。事前に学習されたモデルを使わない場合は、まずランダムに重みを初期
化します。

次に、spaCy は `nlp.update` を呼び出します。これは現在の重みでバッチデータを予測
します。

次に、予測結果を正解と照合し、より良い予測ができるような重みの変更方法を計算しま
す。

最後に、現在の重みを少し更新して、次のバッチデータにうつります。

spaCy はデータの各バッチに対して `nlp.update` を呼び出し続けます。

---

# トレーニングはどのように行われるか(2)

<img src="/training.png" alt="トレーニングプロセスのダイアグラム" />

- **学習データ:** データとそのアノテーション
- **テキスト:** モデルがラベルを予測する入力データ
- **ラベル:** モデルが予測するラベル
- **勾配:** 重みの変更方法

Notes: ここに、処理の図を示します。

学習データは、モデルを更新するもととなるデータです。

テキストは、文章、段落、またはより長い文章です。良い結果を得るためには、モデルを
実際に適用するデータと似たものを用いる必要があります。

ラベルはモデルに予測させものです。テキストのカテゴリ、または固有表現のスパンとそ
のタイプなどが当てはまります。

勾配は、予測と正解の誤差を減らすためのモデルの変更方法を示します。予測ラベルと正
解ラベルを比較するときに計算されます。

学習後、更新されたモデルを保存して、アプリケーションで使用することができます。

---

# 例: 固有表現抽出器の更新

- 固有表現抽出器は、文脈に応じて単語やフレーズをタグ付けします
- 各トークンは 1 つの固有表現の一部にしかなり得ません
- データは文脈に沿ったものである必要があります

```python
doc = nlp("iPhone Xが発売される")
doc.ents = [Span(doc, 0, 2, label="GADGET")]
```

- 固有表現のないテキストも重要です

```python
doc = nlp("新しいスマホが欲しい！どうしたらいい？")
doc.ents = []
```

- **Goal:** モデルを汎化させる

Notes: ここでは、固有表現抽出器の例を見てみましょう。

固有表現抽出器は、Doc を受け取り、**文脈を見て**フレーズとそのラベルを予測します
。つまり訓練データには、テキスト、それらに含まれる固有表現、そのラベルが含まれて
いる必要があります。

固有表現は重複することができないため、各トークンは 1 つの固有表現の一部にしかな
りません。

最も簡単な方法は、モデルにテキストと固有表現のスパンのリストを渡すことです。
spaCy は`doc.ents`にアノテーションを格納した普通の `Doc`を使って学習できます。例
えば、「iPhone X」はガジェットで、0 トークン目で始まり 2 トークン目で終わります
。

固有表現ではない単語を学習することも非常に重要です。

この場合、アノテーションのリストは空になります。

私たちの目標は、たとえ学習データになかったとしても、似たような文脈で新しい固有表
現を抽出するようにモデルを学習させることです。

---

# 学習データ

- モデルが文脈を見て予測してほしいことの例
- **既存モデル**の更新：数百から数千の例
- **新しいカテゴリ**のトレーニング：数千から数百万の例
  - spaCy の英語モデル：200 万語
- 通常は人間のアノテータが手動で作成
- 半自動化することができます。例えば、spaCy の `Matcher` を使用してください！

Notes: 学習データを用いて、モデルに予測させたいことを伝えます。これには、認識し
たいテキストや固有表現、あるいはトークンとその正しい品詞タグなどがあります。

既存のモデルを更新するには、数百から数千のデータが必要です。

新しいカテゴリを訓練するには、数百万程度必要なこともあります。

spaCy の事前学習の英語のパイプラインは、品詞タグと依存関係と固有固有表現のついた
200 万語のデータで訓練されています。

訓練データは通常、人間によって作成されます。

これは大変な作業です。しかし、半自動化することができます。例えば、spaCy の
`Matcher`を使用します。

---

# 学習データと検証データの違い

- **学習データ:\*** モデルを更新するためのデータ
- **検証データ:**
  - モデルにとっては未見のデータで
  - モデルの精度を計算するために使われる
  - モデルが実行中、実際に見るデータを代表すべき

Notes: モデルを訓練するとき、学習がうまくいっているか、正しいことを覚えているか
を確認することが重要です。学習で見ていないデータに対しての予測を正解データと比較
することで、学習が正常に進んでるかどうかを確認できます。そのためには学習とは別に
検証データが必要となります。

検証データはモデルの精度を計算するために使われます。例えば、精度が 90%ということ
は、検証データの 90%の例に対して予測が正しかったという意味です。

検証データはモデルが使用中実際対象となるデータを代表しなければなりません。検証デ
ータと本番環境でのデータが乖離してしまうと、学習中に計算した精度は参考になりませ
ん。

---

# 学習データを生成する(1)

```python
import spacy

nlp = spacy.blank("ja")

# Docと固有表現のスパンを作成
doc = nlp("iPhone Xが発売される")
doc.ents = [Span(doc, 0, 2, label="GADGET")]
# 固有表現のスパンがないDocを作成
doc2 = nlp("新しいスマホが欲しい！どうしたらいい？")

docs = [doc1, doc2]  # などなど
```

Notes: spaCy の学習データは、出力と同じ `Doc` オブジェクトを使います。第 2 章
で`Doc`と`Span`について学びました。

この例では、コーパスのために 2 つの `Doc` オブジェクトを作成しています。 1 つに
は 1 個の固有表現があり、もう 1 つには固有表現がありません。 `Doc`の固有表現を設
定するために、`doc.ents`に`Span`を追加します。

もちろん、モデルがちゃんと汎化して類似している固有表現を予測するにはこれよりたく
さんの学習データを用意しなければなりません。タスクによって、最低数百から数千件の
代表的な例文が必要となります。

# 学習データを生成する(2)

- データを 2 つの部分に分ける
  - **学習データ:** モデルの更新に使う
  - **検証データ:** モデルの精度評価に使う

```python
random.shuffle(docs)
train_docs = docs[:len(docs) // 2]
```

Notes: 以前も書いたように、学習データ以外のデータも必要です。学習中に見ていない
データを使い、精度を評価する必要があります。通常、評価のためにデータをシャッフル
し、学習データと検証データの 2 部分に分けて使います。ここでは単純に半分半分に分
けます。

# 学習データを生成する(3)

- `DocBin`: `Doc`を効率的に保存するためのコンテナ
- バイナリファイルに保存できる
- バイナリファイルは訓練に用いる

```python
# 学習データを作成し、保存する
train_docbin = DocBin(docs=train_docs)
train_docbin.to_disk("./train.spacy")
# 検証データを作成し、保存する
dev_docbin = DocBin(docs=dev_docs)
dev_docbin.to_disk("./dev.spacy")
```

Notes: 通常、spaCy の訓練で使う学習と検証データはバイナリファイルとして保存しま
す。

`DocBin`は`Doc`オブジェクトを効率的に格納・保存するためのコンテナです。 `Doc`オ
ブジェクトのリストで初期化し、`to_disk`メソッドでバイナリファイルに保存できます
。このとき保存したファイルの拡張子は通常`.spacy`としています。

共有語彙を 1 つだけ保存することで、`DocBin`は`pickle`など他の直列化（シリアライ
ズ）のプロトコルと比べて処理が高速で、出力ファイルも小さいです。詳細について
は[資料](https://spacy.io/api/docbin)を参照してください。

---

# ヒント： 学習データの変換方法

- `spacy convert`でよくある形式を変換できる
- `.conll`, `.conllu`, `.iob` や spaCy の旧版の JSON 形式に対応

```bash
$ python -m spacy convert ./train.gold.conll ./corpus
```

Notes: 場合によって、すでに CoNLL や IOB などよくある形式の学習データを持ってい
るかもしれません。 spaCy の`convert`コマンドを使うことで、そのファイルを spaCy
のバイナリファイルに変換できます。また、spaCy v2 で使われた JSON 形式のファイル
も同じコマンドで変換できます。

# Let's practice!

Notes: さて、いよいよ学習データの準備に取り掛かりましょう。いくつかの例を見て、
新しい固有表現タイプの小さなデータセットを作成してみましょう。
