StatsBeginner: 初学者の統計学習ノート

統計学およびR、Pythonでのプログラミングの勉強の過程をメモっていくノート。たまにMacの話題。

DeepSeekが深読みしすぎていて面白い

推論能力(多段階のロジックなどが必要な場合でも正しい答えを出す能力)の高さを謳っている最近のLLMは、ユーザーの質問に対してすぐに回答を返すのではなく、いったん生成した回答について内面的な反省を繰り返してから、ユーザーに返答するようになっていると言われます。OpenAIのo1などはそのプロセスを伏せているのですが、DeepSeekはその部分も「内なるつぶやき」みたいな感じで全て可視化してくれているので、見ていて面白いです。



たとえばさっき、「以下の数列の次の値を予想してください。全て日本語で考えてください。」という質問を打ち込んで、具体的にどのような数列なのか与えるのを忘れてしまいました。
そしたらDeepSeekが悩みすぎて、1分半ぐらいかけて、以下のようにめちゃめちゃ長い「内なるつぶやき」が出てきました。
「ただし、もしかしたらトリックのような問題かもしれません。」から、異常な深読みが始まります。

では、問題を解くために、まずは与えられた数列のパターンを見つけ出そうと思います。でも、ここで一つ問題があります。ユーザーが具体的な数列を提示していないんです。これでは通常、次の項を予測することはできません。

もしかしたら、ユーザーは質問の入力時に数列の数値を書き忘れたのかもしれません。例えば、「1, 3, 5, 7, 9...」のような数列を想定していた可能性があります。あるいは、別のパターンがある数列かもしれません。でも、具体的な数字がないので、どうしようもありません。

まず、ユーザーに数列の具体的な数値を確認する必要があるでしょう。しかし、現在の状況では、ユーザーが日本語で考えて回答するように指示しているので、その流れに沿って対応しなければなりません。

考えられる可能性として、ユーザーが質問の際に数列を入力し忘れたということがあります。あるいは、以前の会話で既に数列が提示されていて、その続きを尋ねているのかもしれません。しかし、この会話履歴を見る限り、そのような情報はありません。

そこで、このような場合の対処法として、一般的な数列の例を挙げて説明するか、あるいはユーザーに数列の具体的な数値を尋ねる必要があります。しかし、ユーザーは「以下の数列」と言っているので、おそらく何らかの数列が存在するはずです。しかし、実際のメッセージには数列が記載されていません。これは入力ミスの可能性が高いです。

ただし、もしかしたらトリックのような問題かもしれません。例えば、「以下の数列」という言葉自体が問題の一部で、例えば「以下」という言葉に関連する数列を考えさせるのかもしれません。例えば、「以下」の文字数を数えると、「以下」は2文字なので、2から始まる数列かもしれません。しかし、これはかなり強引な解釈です。

あるいは、問題文自体が数列の一部になっている可能性もあります。例えば、問題文の文字数や単語数が数列を形成しているかもしれません。問題文を日本語で数えると、「以下の数列の次の値を予想してください。全て日本語で考えてください。」という文章の文字数は、全角文字として数えると、それぞれの文字数はどうなるでしょうか?

まず、問題文を分解してみます。「以下の数列の次の値を予想してください。全て日本語で考えてください。」という文の文字数を数えます。句読点も含めると、以下のようになります:

「以」1

「下」2

「の」3

「数」4

「列」5

「の」6

「次」7

「の」8

「値」9

「を」10

「予」11

「想」12

「し」13

「て」14

「く」15

「だ」16

「さ」17

「い」18

「。」19

「全」20

「て」21

「日」22

「本」23

「語」24

「で」25

「考」26

「え」27

「て」28

「く」29

「だ」30

「さ」31

「い」32

「。」33

つまり、33文字です。しかし、この文字数が数列に関連しているとは思えません。あるいは、単語数かもしれません。「以下の 数列 の 次の 値 を 予想 して ください 。 全て 日本語 で 考え て ください 。」で単語数は15程度ですが、これもパターンを見つけるのは難しいです。

他に考えられるのは、問題文が何かのジョークや言葉遊びである可能性です。例えば、「以下の数列」という部分に注目して、「以下」の「以」の文字コードや画数を使うなどです。「以」の画数は4画、「下」は3画、合わせて7画かもしれませんが、これも推測の域を出ません。

もしくは、ユーザーが何らかのエラーで数列を入力できていない状況です。その場合、回答として数列の不足を指摘する必要がありますが、ユーザーは日本語で考えるように指示しているので、その点を考慮しつつ、丁寧に数列の提示を求めるべきかもしれません。

しかし、現在の状況では、ユーザーが実際に数列を提示していないため、適切な回答ができません。通常、このような場合は「数列が記載されていませんので、具体的な数値を教えてください」と返答するのが適切です。しかし、ユーザーは「全て日本語で考えてください」と指定しているので、その指示に従いながら、問題の不備を指摘する必要があります。

ただし、この問題がテストやクイズとして出題されている場合、例えば「以下の数列」という言葉自体が数列の一部で、言葉の文字数や何らかの規則性がある可能性もゼロではありません。例えば、「以下の数列」の各文字の画数を見てみます:

「以」:4画

「下」:3画

「の」:3画

「数」:13画

「列」:6画

これらを並べると、4, 3, 3, 13, 6 という数列になります。この次の数値を予想する問題かもしれません。しかし、この数列の規則性を見つける必要があります。

4, 3, 3, 13, 6...

この数列のパターンを見つけようとします。まず、4から3へは-1、3から3は±0、3から13は+10、13から6は-7。この差の数列は -1, 0, +10, -7 で、特に規則性が見えません。

あるいは、各数字自体に何か規則があるか。4,3,3,13,6...

4 +3=7, 3+3=6, 3+13=16, 13+6=19… これも不明。

別の角度から、これらの数字を何かのコードと見るか、例えば、画数を別の方法で数える(異なる字体の場合)可能性もありますが、一般的な画数では上記の通りです。

もしくは、各文字の部首の画数など、別の要素を考慮しているかもしれませんが、これも推測が難しいです。

さらに、「以下の数列」という言葉のローマ字の文字数や、何らかの変換を試みることも考えられますが、これも根拠に欠けます。

このように、問題文に数列が提示されていないため、推測が非常に困難です。ユーザーが何らかのミスで数列を入力し忘れたと考えるのが自然ですが、もしこれが意図的な問題であるなら、非常にユニークなケースと言えます。

いずれにせよ、正確な回答のためには数列の具体的な数値が必要です。しかし、現状ではそれがないため、問題の不備を指摘するか、あるいはユーザーが想定していたかもしれない一般的な数列の例を挙げて説明するしかありません。

例えば、典型的な数列問題として、等差数列(2,4,6,8...なら次は10)、等比数列(3,9,27...なら次は81)、階差数列(1,3,6,10...なら次は15)などが考えられます。あるいはフィボナッチ数列(1,1,2,3,5...なら次は8)などです。

しかし、ユーザーが具体的にどの数列を指しているのか不明なため、これらの中から適当に選ぶことはできません。また、問題文の冒頭に「以下の数列」とあるため、何らかの数列が存在するはずですが、実際には記載されていません。

この状況を踏まえると、おそらくユーザーは質問の入力時に数列を添付するのを忘れたか、テキストのコピーでミスが発生した可能性が高いです。したがって、適切な対応としては、数列の提示を求めることが最善です。

ただし、この思考プロセスを日本語で記述することが求められているため、このように問題の不備を指摘しつつ、可能な限り推測を試みるプロセスを説明します。

結論として、数列が提示されていないため正確な回答は不可能ですが、もし典型的な数列を例示するなら、その一例を挙げることはできます。例えば、等差数列の場合、「2, 4, 6, 8...」の次は「10」と予想されます。しかし、これがユーザーの意図した数列かは不明です。

以上のことから、問題文に数列が記載されていないため、次の値を予想できないことを指摘し、数列の入力を求めることが適切だと考えます。


ここまで悩んだ上で、最終的に、

問題文に数列が記載されていないため、次の値を予想できません。数列の具体的な数値をご提供いただければ、そのパターンを分析し次の値を予想いたします。例えば、「2, 4, 6, 8...」のような等差数列であれば次は「10」、「1, 1, 2, 3, 5...」のようなフィボナッチ数列であれば次は「8」と推測されますが、実際の数列がないと正確な回答は不可能です。数列の入力をお願いいたします。


という答えを出してきました。
考えすぎやろw

重み付き回帰で2種類の重みを使いたい時

Rで重み付きの回帰を実行する場合、lmとかlmer(ランダム効果を使う場合)のweights引数に重みを指定すればいいだけですが、2種類の重みを同時に使いたいような場合があります。たとえばパネル調査データで、母集団からのサンプリングのバイアスを補正するための重みと、初回調査から脱落しやすい属性の人たちが過小評価されてしまうのを補正するための重みとか。
考え方はいろいろあると思いますが、とりあえずは2つの重みの積をweightsに指定しておけばいいんじゃないかと思います。
というのも、下記のコードで確認しているように、重み付き回帰を実行するとはすなわち、重みの平方根を元の変数にかけてからふつうに回帰をやるのと同じ(切片項や、ランダム効果の分散にも重みを反映する必要がある点に注意)なので。いったん変数変換で重みを反映したデータにべつの重みを上からかけ直すのと、重み同士をあらかじめ乗じてからかけ直すのは同じ意味になります。
まぁでも、重みの合成の仕方も、データの性質によって考え方がいろいろありそうな気はしますが(重みの平均をとるとか)、勉強してないのでわかりません。積を取る方法だと、重みが極端な値になって扱いづらいことがあるようです。


以下のコードは、weights引数を使う場合と、あらかじめ変数に重みの平方根をかけてから回帰する場合の比較です。(2種類の重みの反映の例ではないです。)

> library(dplyr)
> library(lme4)
> 
> ### lm関数での重み付き回帰の原理の確認
> 
> # 変数を作成する
> set.seed(1)
> x1 <- rnorm(mean=0, sd=3, n=100)
> x2 <- rnorm(mean=3, sd=1, n=100)
> y <- 1 + 2*x1 + 3*x2 + rnorm(mean=0, sd=3, n=100)
> 
> # 重みをつくる
> w <- rexp(n=100)  # 重み変数
> 
> # 重みの平方根
> root_w <- sqrt(w)
> 
> # 重みなしの回帰
> lm(y ~ x1 + x2) %>%
+   coef()
(Intercept)          x1          x2 
   1.557262    2.021110    2.839600 
> 
> # 重み付き回帰(重み付き最小二乗法)
> lm(y ~ x1 + x2, weights=w) %>%
+   coef()
(Intercept)          x1          x2 
   2.839395    1.938573    2.345262 
> 
> # 重みの平方根をかけてから回帰
> yw <- y * root_w
> x1w <- x1 * root_w
> x2w <- x2 * root_w
> 
> # 切片を0で消して、重みの平方根を切片とする
> lm(yw ~ 0 + root_w + x1w + x2w) %>%
+   coef()
  root_w      x1w      x2w 
2.839395 1.938573 2.345262 
> 
> ### lmer関数での重み付き回帰の原理の確認
> 
> # 変数を作成する
> set.seed(1)
> group_id <- factor(rep(1:100, 10))
> group_effects <- rep(rnorm(n=100, mean=0, sd=3),10)
> x <- rnorm(mean=1, sd=5, n=1000)
> y <- 2 + 3*x + group_effects + rnorm(mean=0, sd=3, n=1000)
> 
> # 重みと重みの平方根
> w <- rexp(n=1000)  # 重み変数
> root_w <- sqrt(w)
> 
> # 重みなしの回帰
> lmer(y ~ x + (1 | group_id)) %>%
+   fixef()
(Intercept)           x 
   2.230189    3.009346 
> 
> # 変数に重みの平方根をかける
> yw <- y * root_w
> xw <- x * root_w
> 
> # モデル1:weights引数を使用
> lmer(y ~ x + (1 | group_id), weights=w) %>%
+   fixef()
(Intercept)           x 
   2.033027    2.998279 
> 
> # モデル2:重みの平方根をかけた変数でふつうに回帰
> # まず、切片を0でいたん消去して、root_wを新たな切片として取り入れる
> # ランダム効果については、分散を重みによってスケーリングする。
> # ランダム効果の記法のうち|の前の部分はもともと、分散を表しているので、
> # そこに重みの平方根を入れればよい。
> # グループidにかけるわけではない。
> lmer(yw ~ 0 + root_w + xw + (root_w | group_id)) %>%
+   fixef()
  root_w       xw 
2.033027 2.998279 


ちなみに、lmにweightsを指定した場合は重み付き最小二乗法をやっていて、重み付き二乗誤差を最小化するようになってるはずですが、lmerはデフォルトでは制限付き最尤法(REML)なのでちょっとややこしいです。

Mamba(最近話題の新しいAIアーキテクチャ)の名前の由来

長大なシーケンスを扱うことができ、処理速度が高速で、Transformerを超えるのではないか(今のところケースによって強み弱みはあるらしいけど)と話題になっているMamba(論文リンク)ですが、名前が何の略だったか思い出せなくて確認していたら、そもそも何かの略ではなく、ヘビの名前だそうです。
先日、飲み会中に「ヘビの名前ですか?」と訊かれて「いや、何かの略だったかと」と答えたのですが、ヘビの名前でした。以下は開発者のツイートです。



最後のS4というのは、Mambaのベースとなっている状態空間モデルのStructured State Spaces for Sequence Modelingのことですが、Sの字が蛇みたいに見えるということかな。
で、ブラックマンバというヘビは移動速度がめちゃめちゃ速くて、かつ毒が強力で殺傷能力が高いらしいので、長めの系列データ(シーケンス)がすごい速さでモデルの中を移動する様子や、問題をあっさり片付けてしまう様子をイメージして、このヘビの名前を付けたようです。
Transformerなどの既存モデルを殺してしまうというイメージもあるのかなと思いました(上記ツイートのスレッドの最初を見ると、ヘビがロボットに襲いかかるイラストが添付されている)。

LightGBMにおけるカテゴリ変数の扱い方

LightGBMにおけるカテゴリ変数の扱いについてググっていたら、以下のような投稿があった。
[SOLVED] How exactly does LightGBM handle the categorical features? | Kaggle


この投稿のなかで、いろいろ参考になるリファレンスが貼られている。

max_cat_to_onehot (default = 4, type = int, constraints: max_cat_to_onehot > 0)
when number of categories of one feature smaller than or equal to max_cat_to_onehot, one-vs-other split algorithm will be used
Parameters — LightGBM 4.2.0.99 documentation

LightGBM offers good accuracy with integer-encoded categorical features. LightGBM applies Fisher (1958) to find the optimal split over categories as described here. This often performs better than one-hot encoding.
Advanced Topics — LightGBM 4.2.0.99 documentation

Optimal Split for Categorical Features
It is common to represent categorical features with one-hot encoding, but this approach is suboptimal for tree learners. Particularly for high-cardinality categorical features, a tree built on one-hot features tends to be unbalanced and needs to grow very deep to achieve good accuracy.

Instead of one-hot encoding, the optimal solution is to split on a categorical feature by partitioning its categories into 2 subsets. If the feature has k categories, there are 2^(k-1) - 1 possible partitions. But there is an efficient solution for regression trees[8]. It needs about O(k * log(k)) to find the optimal partition.

The basic idea is to sort the categories according to the training objective at each split. More specifically, LightGBM sorts the histogram (for a categorical feature) according to its accumulated values (sum_gradient / sum_hessian) and then finds the best split on the sorted histogram.
Features — LightGBM 4.2.0.99 documentation


ちなみにFisher(1958)は、斜め読みしかしてないので雰囲気しか把握してないが、1次元の連続値データに基づいてサンプルをグループ分けする際に各グループ内で分散が小さくなるようにするにはどうすればいいかを議論していて、例として200個の数字を10のグループに分ける場合の話がされていた。


ところで、上記投稿者の人自身は、結論を間違えているような気がするな。
この人は、「カテゴリ数(水準数)が`max_cat_to_onehot`以下の場合は、one-vs-other split algorithmが採用され、それより多い場合は、one-hotエンコーディングが採用される」と理解したようなのだが、そもそも水準数が少なければone-hotでいいはずなので、水準数が閾値以下であればone-hot encodingされると理解すべきだろう。


上記3つめの引用に書いてあるように、もともとの問題意識は、カテゴリ数が多い(high-cardinalityな)場合にone-hotエンコーディングをしようとすると非常にアンバランスな木ができてしまい、最適な分割結果に近づくために木を深くしなければならなくなるので、大雑把な分割の中からいいものを選びたいという話である。で、one-vs-other algorythmと呼ばれているものは、one-vs-restと言うことの方が多いようだが、これはようするにone-hotとほぼ同じ意味だと思う。つまり、多クラスの分類を、「あるクラスとそれ以外」に分ける分類器を複数組み合わせて扱うということ。


↓この投稿で検証されているように、`max_cat_to_onehot`よりも少ない水準数の場合は、one-hot encodingした場合と結果が同じになるようだ。
https://stackoverflow.com/questions/65773372/lightgbm-splits-differently-on-the-same-dataset-one-hot-encoded-vs-one-vs-other
(この投稿は、「同じになると思ったらならないんだけど…」という内容で始まって、「あ、バージョン上げたら同じになったわ」と自己解決しているもの。)


で、じゃあカテゴリ数が多い場合にどうなってるのかなのだが、まずそもそも、以下のような「ヒストグラムベース」の木構築の話を頭に入れておかないといけない。


勾配ブースティング決定木ってなんぞや #Python - Qiita
xgboostのコードリーディング(その2) - threecourse’s blog
Kaggleで大人気!勾配ブースティング決定木の軽量モデル「LightGBM」を詳細解説! | DeepSquare


勾配ブースティング決定木においては、ある時点で構築された木に基づく予測値と実際の値から誤差を計算して、誤差関数を「予測値で」偏微分する。つまり、予測値をどっちに動かすと、誤差がどれだけ増えるかを計算する。
ニューラルネットのように、「モデルのパラメータで」偏微分するわけではないので注意が必要。
各ノード(決定木の分岐点)において、特徴空間のどの部分を分割すると、サンプルの勾配の合計が最大限に減少するかを学習していく流れになる。
で、この際、分割点の探索は計算コストが高いので、連続変数ならビンでまとめてヒストグラムのようにした上で、どのビン間で分割するのがよいかを探索することになる。カテゴリ変数(ラベル)の場合は、数量化の工夫を行う必要があって、`(sum_gradient / sum_hessian)`を使ってどうたらこうたらというのは、その話だ。


で、決定木系のアルゴリズムをほとんど勉強してないので単なる想像なのだが、LightGBMにおいて水準数の多いカテゴリ変数扱いは、たぶん以下のような感じなのかな?
全く自信はないのであとで時間あるときに確認しようと思ってるのだけど、誤解だとしても「現時点でどう理解したか」メモっておくのが効率的なので、メモっとくことにするw

  1. 現時点の木の構築:初期の木、またはブースティングの反復を経た現在の木が与えられる。
  2. 全サンプルの勾配とヘシアンの計算:各サンプルについて、勾配(損失関数を予測値で一次偏微分)とヘシアン(損失関数を予測値で二次偏微分)を計算する。
  3. カテゴリ変数を数値に変換:各カテゴリについて、そこに属する属するサンプルの勾配/ヘシアン比の合計値を計算する。この値は、そのカテゴリの重要性を示す。
  4. カテゴリのソート:カテゴリ変数を、上記の重要度に基づいてソートする。
  5. ヒストグラム化:ビンでまとめてヒストグラム化する。
  6. 最適な分割点の探索:Fisher (1958)の手法や他の統計的手法を用いて、どのビン間で分割するのが最も効果的かを探索する。(どう分割すれば勾配をさらに小さくできるかを探索する。)


カテゴリ数が閾値(デフォルトでは4)以下であれば、ふつうにone-hotエンコーディングが採用される。それより大きい場合は、上記のような(誤解ならこれを修正した)手順にしたがって、分割される。なおたぶん、`max_cat_threshold`というパラメータ(デフォルトは32)に設定した値より細かくは分割されないということだと思う。決定木は通常、バイナリ分割(2つに分けていく)で、それを何回か重ねて分けていくが、32分類以上にはしないということだろう。

MacでのChrome AppsのTwitter (X)アプリのインストール

今、Macでツイッター(X)のアカウントを複数運用するなら、Chrome Appsのアプリが最適だと思っていて、それを再度インストール(正確にはMacの別のユーザアカウントにインストール)しようと思ったら方法が分からなくて困ったのでメモしておきます。
説明するほどの手順ではないんですが、ぐぐっても出てこなかったので。
 

  1. ChromeでTwitter(X)にログインする
  2. Chromeの右上の「…」を縦にしたボタンのメニューに「Xをインストール」っていう選択肢があるのでそれをクリック
  3. ローンチパッド上にXのアプリのアイコンが出るのでそれをドックに配置

 
で完了です。
`~/Applications/Chrome Apps/X.app`
という場所に入っています。
ユーザディレクトリの配下なので、
`/Applications`
の中を探しても無いので気をつけてください。
 
ちなみに私は、もともとインストールしていたユーザアカウントではTwitter.appになってて、新しくインストールしたやつはX.appになってます。
 
あとは余談ですが、自分はMac用のツイッタークライアント使用歴は以下のような感じです。

  1. Janetter:一瞬話題になってから何となく使ってたやつで、夜フクロウとかTweetDeckに比べて何がよかったかは忘れた。
  2. Twitter for Mac(公式):結局公式がいちばん使いやすい気がしてしばらく使っていたが、開発会社が無くなったか何かで、2018年に廃止された。2019年に一時的に復活してるしいけどいまいち覚えてない。
  3. Catalyst版:Catalystという、iPad用アプリをMacで使えるようにするフレームワーク(?)によって、Twitter for Mac(公式)が復活。しばらく使ったが、廃止されたか不具合が気になったかで、使うのをやめた。
  4. Multiple Twitter Accounts Chrome(Chrome拡張):Chrome拡張でTwitterアカウントを複数運用するツールがあり、これをつかってブラウザでみるようになったが、イーロン・マスクに買収された後に何かが起きてまともに使えなくなった。(2023年1月のサードパーティ排除騒動かな?)
  5. Chrome Apps版:経緯は忘れたけどChrome Appsのアプリを発見してインストール。現在に至る。

“All You Need”論文のベースモデルに近いスペックの英日翻訳Transformerを作ってみた

先日から英日Transformerの学習結果の報告を何度か書いてますが(エントリ1エントリ2エントリ3)、AWSでA100というGPUが8枚使える最高スペックのインスタンスが空いたので*1、コーパスとモデルを少し大きくして、Googleの有名な“Attention is all you need”論文のベースモデルに近い規模にしてみました。


具体的にいうと、All You Need論文では英語-ドイツ語の翻訳に関しては、

  • 450万ペアの対訳コーパス
  • 37000トークンのボキャブラリ辞書(byte-pair encoding)
  • Transformerブロックはエンコ側・デコ側ともに6層
  • 埋め込みは512次元
  • マルチヘッドアテンションのヘッド数は8
  • フォードフォワード部分の隠れ層は2048次元
  • 10万ステップ学習(12時間)


というスペックになっています。ステップは、サンプルサイズ÷バッチサイズという理解であってるかな?


私が今回構築したのは、英語-日本語の翻訳モデルで、

  • 580万ペアの対訳コーパス
  • 30000トークンのボキャブラリ辞書(byte-pair encoding)
  • Transformerブロックはエンコ側・デコ側ともに6層
  • 埋め込みは512次元
  • マルチヘッドアテンションのヘッド数は8
  • フォードフォワード部分の隠れ層は2048次元
  • 10万ステップ学習(25時間)したが勾配累積してるのでパラメータの更新回数はその4分の1になる


という規模です。前回からの変化としては、コーパスの規模が170万から580万に増えたのと、フィードフォワードの次元が512だったのを2048にしています。ちなみに使っているコーパスは、以下のような無料のものをクリーニングして合体して使っています。

  • TED字幕(元データは15万8535件/うち15万5723件使用)*2
  • 青空文庫等の小説(元データは11万8143件/クリーニングとデータ拡張処理を経て11万1485件使用)*3
  • 京都Wiki(元データは44万3849件/うち13万4199件使用)*4
  • 映画字幕(元は280万1388件/うち125万5207件使用)*5
  • 田中コーパス(元データは14万7918件/うち14万4683件使用)*6
  • 法令対訳コーパス(元データは26万2449件/うち17万6054件使用)*7
  • JparaCrawl(元データは2574万0835件/うち128万4193件使用)*8
  • Wikimatrix(元データは85万1706件/うち58万0375件使用)*9
  • Coursera(元データは5万3166件/うち5万2614件使用)*10
  • OSSマニュアル(元データは40万3366件/うち26万2067件使用)*11
  • ASPEC(元データは300万8500件/うち165万9318件使用)*12


元データの件数は、英語か日本語のどちらかが欠けているデータを除いてカウントしているので、公式の表記より少ない場合があります。これらのコーパスを全部合わせると3398万9855件あるのですが、データを絞って使っています。
まず、全体として英語文10〜500文字、日本語文10〜250文字のものに限ってます。本当は長い文も学習させたいのですが、バッチ内で最長のシーケンスに合わせてパディングされる影響もあって、長い文が少しでも混じってるとメモリを大きく圧迫するので、我慢しました。
またコーパスによっては、日本文に英数字をたくさん含むもの、日本文が句点で終わってないもの、英文が大文字で始まってないもの、日本文と英文の文字数の比率が極端なものなどを除きました。句点で終わってないとかは、それ自体は大した問題ではないのですが、「品質の低い対訳文例」である可能性が少し高いので除いているという感じです。
もともとデータ量が多いコーパスについては、日本語が50文字未満のものをバッサリ削ったりもしてます。どちらかといえば長めの学習データが不足しがちだからです。
句読点を補ったり、日本語が分かち書きされていたのでスペースを削って詰める操作をしたコーパスもあります。


前回、前々回と翻訳結果を比較してみます。コーパスの規模を130万ペア、170万ペア、580万ペアと増やしてきたので、1.3M、1.7M、5.8Mと表記してます。個人的に一番よかったものに★印を付けています。

Your time is limited, so don’t waste it living someone else’s life. (Steve Jobs)
【1.3M】時間は限られているので、他人の人生を浪費しないでください。
【1.7M】時間が限られているので、他人の人生を無駄にしないでください(スティーブ・ジョブズ)。
【5.8M】時間は限られているので、他の人の人生を生きることは無駄にしないでください。
(拙訳:君たちの時間は限られている。他人の人生を生きるようなことをしてそれを無駄にするな。/スティーブ・ジョブズ)
 
I have a dream that my four little children will one day live in a nation where they will not be judged by the color of their skin but by the content of their character. (Martin Luther King Jr)
【1.3M】私は4人の小さな子供たちが、いつか、自分の肌の色ではなく、その性格の特徴によって判断されない国家に住む夢を持っています。
【1.7M】わたしには、ある日、わたしの4人の小さな子供が、肌の色ではなく、その性格の色によって判断されるであろう国に住んでいるという夢があります(マーティン・ルーサー・キングジュニア)
【5.8M】ある日、私の4人の子供たちが、自分の肌の色ではなく、その性格の含みで判断される国に住んでいくという夢があります(マルティン・ルーサー・キング・ジュニア)。
(拙訳:私には夢がある。私の4人の子供たちがいつの日か、肌の色ではなく彼らの人格によって判断される国に暮らせるようになるという夢が。/キング牧師)
 
You are fake news! (Donald Trump)
【1.3M】偽のニュースです!
【1.7M】ばかばかしいニュースだ!(ドナルド・トランプ)。
【5.8M】偽りのニュースです!(ドナルド・トランプ)
(拙訳:フェイクニュース屋め!/トランプ大統領)
 
You may say I'm a dreamer. But I'm not the only one. I hope someday you'll join us. And the world will be as one. (John Lennon)
【1.3M】夢想家だって言うかもしれないけど、私だけじゃない。いつか、ぼくらと合流してくれるといいんだけど。そして世界は、ひとつのものになる。
【1.7M】夢想家だと言うかもしれません。でも、私はただ一人ではありません。いつかあなたたちが仲間になるといいですね。世界は1つになるでしょう。(ジョン・レノン)
【5.8M】夢想家だと言ってもいいけど、わたしだけじゃない。いつかあなたも参加してくれるといいんだけど。そして世界は一つになるよ(ジョン・レノン)。
(拙訳:夢想家だと君は言うかもしれないけれど、僕は一人じゃない。いつか君も一緒になれれば。そして世界は一つになる。/ジョン・レノン)
 
The madman is not the man who has lost his reason. The madman is the man who has lost everything except his reason. (G. K. Chesterton)
【1.3M】狂人は理性を失った人間ではない。狂人は、理性以外はすべてを失った男だ。
【1.7M】狂人は理性を失った人間ではない。狂人は理性以外のすべてを失った男である(G.K.チェスタトン)。
【5.8M】狂人は、その理性を失った人ではない。狂人は、その理性を除いて、すべてを失った人だ。(G.K.チェスタトン)
(安西徹雄訳:狂人とは理性を失った人のことではない。狂人とは、理性以外のあらゆる物を失った人のことである。/チェスタトン)
 
The safest general characterization of the European philosophical tradition is that it consists of a series of footnotes to Plato. (A. N. Whitehead)
【1.3M】ヨーロッパ哲学伝統の最も安全な一般的な特徴は、それがプラトンへの一連の脚注から構成されていることです。
【1.7M】ヨーロッパの哲学的伝統の最も安全な一般的な特徴は、プラトンへの一連の脚注から成り立っていることである(A.N.ホワイトヘッド)。
【5.8M】ヨーロッパ哲学の伝統の中で最も安全な一般の特徴は、プラトンへの一連の脚注からなるということである。
(拙訳:ヨーロッパ哲学の伝統について間違いなく言えるのは、その全てが、プラトン哲学へのひと続きの注釈に過ぎないということである。/ホワイトヘッド)
 
Violence sometimes may have cleared away obstructions quickly, but it never has proved itself creative. (Albert Einstein)
【1.3M】暴力は時々すぐに妨害を片付けるかもしれませんが、それは創造的に証明されたことはありません。
【1.7M】暴力沙汰はすぐに閉ざされたのかもしれないが、しかし、創造的自立は証明されていない(アインシュタイン)
【5.8M】暴力はときどき障害をなくしてしまったかもしれないけれど、創造的だと証明されることはなかった。
(拙訳:暴力が、問題を手っ取り早く片付けるのに役立つことはある。しかし、暴力それ自身が創造的であったことは一度もない。/アインシュタイン)


正直、そんなによくなったわけではないですねw
今回追加したコーパスのうち、OSSマニュアル(オープンソース・ソフトウェアの説明書の対訳データ)とASPEC(学術論文のアブストラクトの対訳データ)は、内容が専門的なので、言葉のチョイスがそれに引っ張られてしまったかもしれません。


ただ前回も言いましたが、当初はキング牧師の引用文中のbutがうまく反映されず、「肌の色ではなく人格によって判断される」というふうに訳すことが安定的にはできてなかったのですが、今回はbutの解釈自体は総じて的確でした。↑の比較では前回(1.7M)のが一番良いのですが、これはたまたま最終エポックのモデルでうまく訳せていただけという感じだったのですが、今回は下記のように各エポックごとのモデルの結果を並べて見ても、butの解釈(○○ではなく××)自体は安定しています。
contentの訳が安定してなかったり、「one day」を1.3Mの時は「いつか」と訳せてたのに「ある日」とかになってるのは残念ですが……。
一番よかったのはエポック27のやつですかね。

Epoch01: 私は、私の子供達が、その子供達が、その1週間前に、その子供たちの色を、その色に、その色に、その色が、その色(英語版)のスタイルに、その色が、その色に、その色が*13

Epoch02: 私は私の4つの小さな子供が、彼らの皮膚の色によって判断されない国家に住んでいる夢を持っていますが、彼らのキャラクターのコンテンツ(ミートリン・ルーズ・キング・ジョル)によって。

Epoch03: 私は私の4つの小さな子供たちが1日1日に、彼らの肌の色で判断されない国家に住んでいる夢を持っていますが、彼らのキャラクターのコンテンツによって。(マテイン・ルター・キング・Jr)。

Epoch04: 私は私の4人の子供たちが1日1日に、彼らの肌の色によって判断されない国家に住んでいる夢を持っていますが、彼らのキャラクターのコンテンツ。(マティニンルーサーキングJr)。

Epoch05: 私は私の4人の小さな子供たちが1日1日に、彼らの肌の色によって判断されない国家に住んでいる夢を持っていますが、彼らの性格のコンテンツ。(マティインルーサーキングJr)。

Epoch06: 私は私の4人の子供たちが1日1日に国民に住み、彼らは彼らの肌の色によって判断されないが、彼らの性格のコンテンツによって。(マリン・ルター・キング・ジュニア)私は夢を持っています。

Epoch07: 私は私の4人の小さな子供たちが1日、彼らの皮膚の色ではなく、彼らの性格のコンテンツによって判断される国に住んでいる夢を持っています。(マリンルーサーキングJr)。

Epoch08: 私は私の4人の小さな子供たちが1日、彼らの肌の色によって判断されないが、彼らの性格の内容によって、国民に住んでいる夢を持っています。(マリンルーサーキングJr)。

Epoch09: 私は私の4人の子供たちが1日、彼らの肌の色によって判断されないが、彼らの性格の内容によって決定される国に住んでいる夢を持っています。(マリン・ルーサー・キングJr)。

Epoch10: 私は私の4人の子供たちが1日、彼らの肌の色によって判断されないが、彼らの性格の内容によって、彼らが国に住んでいることを夢見ています。(マータンルーサーキングJr)。

Epoch11: 私は私の4人の子供たちが1日、彼らの肌の色によって判断されるのではなく、彼らの性格の内容によって決定される国に住んでいる夢を持っています。(マータンルーサーキングJr)。

Epoch12: 私は私の4人の子供たちが1日、彼らの肌の色ではなく、彼らの性格の内容によって判断される国に住んでいるという夢を持っています。(マルティンルーサーキングJr)

Epoch13: 私は私の4人の小さな子供たちが1日、彼らの肌の色ではなく、彼らの性格の内容によって判断される国に住んでいるという夢を持っています。(マルティンルーサー王Jr)

Epoch14: 私は私の4人の子供たちが、ある日、彼らの肌の色ではなく、その性格の内容によって判断される国に住んでいる夢を持っています。(マルティンルーサー・キングJr)

Epoch15: 私は私の4人の子供たちが、ある日、彼らの肌の色ではなく、その性格のコンテンツによって判断される国に住んでいるという夢を持っています。(マルティンルーサーキングJr)

Epoch16: 私の4人の子供たちが、ある日、その人の肌の色ではなく、その人の性格の形によって、その人の肌の色によって判断されるような国に住む夢があるのです。

Epoch17: 私はある日、私の4人の子供たちが、その人の肌の色ではなく、その人の性格の含みによって、その国の生活をするという夢を持っています。

Epoch18: 私は、私の4人の小さな子供たちが、ある日、彼らの肌の色ではなく、その性格のコンテンツによって判断される国に住んでいる夢を持っています。(マルティン・ルーサー・キングJr)

Epoch19: 私は、私の4人の子供たちが、ある日、彼らの肌の色ではなく、その性格のコンテンツによって、彼らが判断される国に住んでいるという夢を持っています。

Epoch20: 私は、私の4人の小さな子供が、ある日、彼らの肌の色ではなく、その性格の内容によって判断される国に住むことを夢見ています(マルティン・ルーサー・キング・ジュニア)。

Epoch21: 私は、私の4人の小さな子供が、ある日、彼らの肌の色ではなく、その性格の内容によって判断される国に住んでいるという夢を持っています。(マルティン・ルーサー・キングJr)

Epoch22: 私は私の4人の子供たちが、ある日、彼らの肌の色ではなく、その性格の含みによって、彼らが判断される国に住んでいるという夢を持っています。(マルティン・ルーサー・キングJr)

Epoch23: 私は私の4人の子供たちが、ある日、彼らの肌の色ではなく、その性格のコンテンツによって判断される国に住むことを夢見ています(マルティン・ルーサー・キングJr)。

Epoch24: 私は私の4人の子供たちが一日住んでいて、その国の色によって判断されるのではなく、その性格のコンテンツによって判断されるという夢を持っています。

Epoch25: 私は私の4人の子供たちが、ある日、彼らの肌の色ではなく、その性格のコンテンツによって判断される国に住むことを夢見ています(マーティン・ルーサー・キングJr)。

Epoch26: 私は私の4人の子供たちが一日住んでしまう夢を持っています。彼らは彼らの肌の色ではなく、その性格の内容によって判断されるでしょう。

Epoch27: 私は私の4人の子供たちが、ある日、彼らの肌の色ではなく、その性格の内容によって判断される国に住むことを夢見ています(マーティン・ルーサー・キングJr)。

Epoch28: ある日、私の4人の子供たちが、自分の肌の色ではなく、その性格の含みで判断される国に住んでいくという夢があります。

Epoch29: ある日、私の4人の子供たちが、自分の肌の色ではなく、その性格の含みで判断される国に住んでいくという夢があります。

Epoch30: ある日、私の4人の子供たちが、自分の肌の色ではなく、その性格の含みで判断される国に住んでいくという夢があります(マルティン・ルーサー・キング・ジュニア)。


「性格のコンテンツ」という訳になっているのは、今回追加したコーパスの癖のような気がします。ソフトウェアのマニュアルと学術論文のコーパスを追加したのですが、英語をカタカナで表現してる部分が多くて、その影響が出たのかなと。


以下は学習曲線で、一応検証ロスは微妙に下がり続けているのですが、上記の各エポックの翻訳結果の変化を見るかぎり、後半はあまり頑張っても意味ない気はしますね。



数百万件程度の学習しかしてないので、超大規模にしたときにどうなのかはわからないのですが、「きれいなコーパスをバランスよく使うこと」がめちゃ重要だなと痛感してます。上の翻訳の例を見ても、Transformerはさすがという感じで、両言語間の関係の把握や出力文の文法的な正しさとかの面ではいい仕事をしてるのですが、出力文の単語のチョイスにセンスの無さを感じるので、このへんはコーパス依存の部分が大きいんじゃないかなと。

*1:AWS側のリソースが逼迫しているらしく、運よく空いてる時間に当たらないと起動できない。

*2:TEDの字幕で、品質はとてもいいと思う。

*3:小説のデータで、訳の品質はいいと思うが、英日どちらも文学作品として成り立つように書かれた文章なので、言語的意味が逐一対応しているわけではない気もする。他のコーパスにない特徴として、内容的に連続した文が並んでいるので、連結することで長文のサンプルを作ることができる。

*4:京都に関係するWikipediaのコンテンツを訳したものらしい。訳の品質はいいと思うが、話題が偏っているのと、Wikipediaなので翻訳の学習に適さない情報も混じっている。

*5:色々な映画の字幕の対訳データで、品質はよくデータ量も多くていいのだが、映画の字幕なので1文1文は短め。日常会話のサンプルを稼ぐことができて良いと思う。意訳が多い点がどう出るか。

*6:品質は良いが1文1文は短い。

*7:日本の法令の対訳データ。翻訳自体の品質はよく、また比較的長めのセンテンスが多いところが良いが、内容は当然偏っている。

*8:NTTの研究所がWebクローラを使って自動収集したコーパス。データ量は多いが、変なデータもかなり混じっている印象。

*9:FacebookがWikipediaから作っている多言語の対訳コーパスのうち、英-日分を抜き出したもの。対訳の品質はけっこう良い。

*10:教育分野の話し言葉の対訳コーパスで、品質的にはとても綺麗。1文1文が短いのと、データ量が少ないのが惜しい。

*11:オープンソースのソフトウェアの説明書の対訳データ

*12:学術論文のアブストラクトのデータ。オンライン申請が必要だがすぐにもらえます。

*13:RNNでもそうなのだが、ディープラーニングによる機械翻訳はそれまでの出力結果を受けて次の1語を予測するという流れになっていて、学習が不十分だと、新しい出力を足しても文全体の意味があまり変わらないような場合に同じような表現が繰り返し吐き出される。初期のDeepLで訳文になぜか全く同じ文が連続して入っていたりしたのも同じ理由。

英日翻訳Transformerを少しだけ大きくしてみた結果

研究でTransformerを使いたいので(去年ちょっと実際に使いましたが)、基本から勉強しようと思い、先日自分でTransformerに英日翻訳を学習させてみたのですが(エントリ1エントリ2)、GPUを複数枚並列で動かせる環境を手に入れたので、コーパスとモデルを少し大きくしてみました。
対訳ペア数は、128万から175万へ(Wikimatrixという新しいコーパスを追加)。
Transformerのブロック数を、両側3層から両側6層へ。
ただしByte-Pair Encodingの語彙数を、3万から2万に減らしました。なぜか新しい環境では3万でやるとクラッシュして動かなかったためです。なお、GoogleのAll You Need論文では、確か英独翻訳が数百万、英仏翻訳で数千万の対訳ペアが使われているのに対して、BPEの語彙数は3万ちょい(多言語横断でこのサイズ)だったと思うので、2万でも十分だとは思っています。BPEの語彙数はイメージより少なくて大丈夫だよというのは、よく言われているようです。
Transformerブロックを2倍にしたけど語彙数を小さくしたので、パラメータ数はあまり変わりません(前回が6900万、今回が7100万)。


50エポックまで学習させました。途中でGPUを4枚から8枚に増やしたら、グラフのとおりなぜか訓練誤差が跳ね上がったのですが、ちょうど2倍なので、どこかで平均か何かをとる操作が間違ってる気がします。検証誤差は下がっているので気にしないことにしましたw



以下が、前回と今回の機械翻訳の結果の比較ですが、大差はないという感じです(笑)
やはりコーパスをデカくしないとだめなのかも知れません。

Your time is limited, so don’t waste it living someone else’s life. (Steve Jobs)
【前回】時間は限られているので、他人の人生を浪費しないでください。
【今回】時間が限られているので、他人の人生を無駄にしないでください(スティーブ・ジョブズ)。
(拙訳:君たちの時間は限られている。他人の人生を生きるようなことをしてそれを無駄にするな。/スティーブ・ジョブズ)
 
I have a dream that my four little children will one day live in a nation where they will not be judged by the color of their skin but by the content of their character. (Martin Luther King Jr)
【前回】私は4人の小さな子供たちが、いつか、自分の肌の色ではなく、その性格の特徴によって判断されない国家に住む夢を持っています。
【今回】わたしには、ある日、わたしの4人の小さな子供が、肌の色ではなく、その性格の色によって判断されるであろう国に住んでいるという夢があります(マーティン・ルーサー・キングジュニア)
(拙訳:私には夢がある。私の4人の子供たちがいつの日か、肌の色ではなく彼らの人格によって判断される国に暮らせるようになるという夢が。/キング牧師)
 
You are fake news! (Donald Trump)
【前回】偽のニュースです!
【今回】ばかばかしいニュースだ!(ドナルド・トランプ)。
(拙訳:[CNNは]フェイクニュースだ!/トランプ大統領)
 
You may say I'm a dreamer. But I'm not the only one. I hope someday you'll join us. And the world will be as one. (John Lennon)
【前回】夢想家だって言うかもしれないけど、私だけじゃない。いつか、ぼくらと合流してくれるといいんだけど。そして世界は、ひとつのものになる。
【今回】夢想家だと言うかもしれません。でも、私はただ一人ではありません。いつかあなたたちが仲間になるといいですね。世界は1つになるでしょう。(ジョン・レノン)
(拙訳:夢想家だと君は言うかもしれないけれど、僕は一人じゃない。いつか君も一緒になれれば。そして世界は一つになる。/ジョン・レノン)
 
The madman is not the man who has lost his reason. The madman is the man who has lost everything except his reason. (G. K. Chesterton)
【前回】狂人は理性を失った人間ではない。狂人は、理性以外はすべてを失った男だ。
【今回】狂人は理性を失った人間ではない。狂人は理性以外のすべてを失った男である(G.K.チェスタトン)。
(安西徹雄訳:狂人とは理性を失った人のことではない。狂人とは、理性以外のあらゆる物を失った人のことである。/チェスタトン)
 
The safest general characterization of the European philosophical tradition is that it consists of a series of footnotes to Plato. (A. N. Whitehead)
【前回】ヨーロッパ哲学伝統の最も安全な一般的な特徴は、それがプラトンへの一連の脚注から構成されていることです。
【今回】ヨーロッパの哲学的伝統の最も安全な一般的な特徴は、プラトンへの一連の脚注から成り立っていることである(A.N.ホワイトヘッド)。
(拙訳:ヨーロッパ哲学の伝統について間違いなく言えるのは、その全てが、プラトン哲学へのひと続きの注釈に過ぎないということである。/ホワイトヘッド)
 
Violence sometimes may have cleared away obstructions quickly, but it never has proved itself creative. (Albert Einstein)
【前回】暴力は時々すぐに妨害を片付けるかもしれませんが、それは創造的に証明されたことはありません。
【今回】暴力沙汰はすぐに閉ざされたのかもしれないが、しかし、創造的自立は証明されていない(アインシュタイン)
(拙訳:暴力が、問題を手っ取り早く片付けるのに役立つことはある。しかし、暴力それ自身が創造的であったことは一度もない。/アインシュタイン)


キング牧師の文章を、「肌の色では判断されない」が「人格で判断される」という対比で訳すのがなかなか成功しないのですが、今回その点は出来ています。「性格の色」という訳は変なのですが、前回はこのbutの対比が1度も正しく訳せなかったので、すこしは進歩したのかも知れません。
ただ、さっき気づいたのですが、英文のJrのところにピリオドが抜けているので補ってJr.としてみたところ、訳文がまたおかしくなったので、これはたまたま訳せただけで、まだまだ安定してないです(笑)
まぁそれにしても、訳が正しいかどうかはともかく、この程度の規模の学習でも文法的に整った文がちゃんと生成されるという点で、Transformerはやはりすごいと思えますが。


ちなみにDeepLは完璧に訳してくれます。

私の4人の小さな子供たちが、いつの日か肌の色で判断されるのではなく、人格の中身で判断されるような国に住むことを夢見ている。(キング牧師)


なお、学習途中のモデルの性能を観察してみると、ある程度学習が進んだあとは、「この文はうまく訳せるようになったけど、こっちはむしろだめになった」みたいなことの繰り返しなので、もっと早めに打ち切ってもあまり変わらない気はしますね。


今回はどちらかというと、GPUを並列化した学習のテストのつもりだったので、後日、コーパスを400万件ぐらいに増やしてリトライするつもりです。