[初級編]LLMへ至る道~損失関数ってなにをしているの?~[2日目]

2023.12.02

みなさんこんにちは!クルトンです。

前日のブログは初回という事で、「機械学習とはなんぞや」について説明しました。

本日のブログは損失関数についてです。

なぜ2日目に持ってきたかというと、機械学習の理解において損失関数が重要であると考えているためです。

データの特徴によって機械学習で使うモデル(アルゴリズム)の種類が違うという話をしている書籍やブログはあります。 ただし本アドベントカレンダーでは、人間の言葉を理解する機械学習モデルについて説明しようと考えているため、対象外のモデルも説明に含めてしまいます。

また実際に、さまざまな機械学習モデルを動かす必要がある方でなく、対象読者を「(ChatGPTなどLLMについて)どういう動きをしているか中身を知りたいなー」という人からするとなくても良い情報なのではないか?と考え、思い切ってそちらは説明せずに機械学習モデルなら共通で必要な内容の中でいくつかに絞って本日からご説明していきます。

損失関数ってなんだ?

早速タイトルにもある損失関数についてです。

機械学習を動かす際には、コンピュータ内部で動かすため、さまざまな情報を数値で取り扱う必要があります。例えば画像であっても、人間の言葉であっても、内部では数値に変換してコンピュータに分かる言葉にする必要があります。

road-to-llm-advent-calendar-2023-02-01

コンピュータに分かる言葉(数値化)を使って機械学習モデルが学習できる状態になっても、学習がうまくいっているのか判断する必要があります。

road-to-llm-advent-calendar-2023-02-02

地図アプリ(損失関数)で目的地への方向(学習の向き)が合っているか確認しているようなものだと捉えて頂ければOKです。

学習の方向が合っているか、数値を使って判断しているのが損失関数と呼ばれているものです。

どのように学習の方向性が合っているかを確認するかというと、「正解」と「予測した結果」の差を見ることで、どれだけ学習がうまく進んでいるかを判断しています。

今回は、具体例として2つの損失関数を確認していきます。

  • 2乗和誤差
  • 交差エントロピー誤差

順番にご紹介していきます。

2乗和誤差

2乗和誤差を数式で表すと以下のようになります。

2乗和誤差 = k = 1 n ( y k - t k ) 2

yが予測した結果の値、tが正解のデータです。

上記の数式ですが、Σのマークは1~n番目の対象とする数値を足し算していくものです。足し算の対象とするものは kの添字と共に書かれている (y-t)の2乗 です。右肩に2の数値が書かれているので、(y-t)*(y-t) と掛け算をしています。

したがって数式をもう少し書き下してみると、以下のようになります。

2乗和誤差 = ( y 1 - t 1 ) * ( y 1 - t 1 ) + ( y 2 - t 2 ) * ( y 2 - t 2 ) + + ( y n - t n ) * ( y n - t n )

上記までの内容で (y-t) となっている部分は、正解としている数値と予測の数値の差分を取っていますね。そして差分を取った後に2乗しているのは、マイナスの符号を取るためです。つまり、計算した結果の値が大きければ大きいほど学習が上手くいっていない、計算した結果の値が小さければ小さいほど学習が上手くいっていると判断できます。

もしプログラミングを実行できる人がいれば以下のようにもPythonコードで書けるので、例として見て頂ければと思います。(プログラミングした事がない方はここまでの内容で、差分を計算しているという意味が分かればOKです。)

import numpy as np

#2乗和誤差(sum of squared error)の実装
def sum_squared_error(y, t):
    return np.sum((y - t) ** 2)

t = np.array([0, 1, 0, 1, 0]) #正解のデータ
y1 = np.array([0.01, 0.65, 0.13, 0.70, 0.15]) # 学習が上手くいっている場合
y2 = np.array([0.34, 0.21, 0.91, 0.33, 0.22 ]) # 学習が上手くいっていない場合

#2乗和誤差の計算を実行
y1_loss = sum_squared_error(y1, t)
y2_loss = sum_squared_error(y2, t)

print(f"学習が上手くいっている時:{y1_loss}")
print(f"学習が上手くいっていない時:{y2_loss}")

上記コードをGoogle Colab(ランタイムはCPU)で実行しました。

sum_squared_error という損失を計算する関数を用意し、サンプルとして正解と予測している結果(2種類)を用意しそれぞれ計算したところ、計算結果は以下のようになりました。

学習が上手くいっている時:0.252
学習が上手くいっていない時:2.0651

今回は自分で定義した値を入れて計算していますが、機械学習モデルの中でも2乗和誤差を使う時はこのように計算しているとイメージが出来たなら幸いです。

交差エントロピー誤差

次に交差エントロピー誤差です。(英語だとcross entropy errorです。)

交差エントロピー誤差 = - k = 1 n t k log   y k

こちらも2乗和誤差と同じく、tは正解の値でyは機械学習モデルが予測した値になります。Σは先ほどと同じ意味です。

logは底をeとする対数です……と書かれた時に、意味が分かる人は次のコードに関する文章に進んでいただけたらと思います。

logについて分からない人は具体的に値をチェックしてみましょう。お手元のスマホ(AndroidでもiPhoneでもOK)で最初から入っている電卓アプリを起動してください。 電卓アプリが起動できたら横画面にしてください。logやlnという表記があるでしょうか?lnと書かれているボタンを使って、具体的な値の確認をしてみます。

例えば学習が上手くいった場合で、yが0.8、tが1とします。 上記の数式と同じく、電卓アプリへ値をマイナスから順番に入れてみてください。 私が手元で確認したところ、0.22314355......となりました。

次に学習が上手くいかなかった場合で、yが0.2、tが1とします。 同様に計算すると、1.60943791243......となりました。

2つの場合を見比べると、正解の値に近い方がより小さい値が出ます。lnでは少数を入れるとマイナスの値が出ますので、先頭にマイナスをかけている訳です。

より詳しくlnについてお知りになりたい方は「ネイピア数e」や「自然対数」などと検索してみてください。

ひとまずは 「正解により近い値を入れるとより小さい値が出てくる」 という所を押さえておけばOKです!

では交差エントロピー誤差についてもPythonコードで確認してみます。実行環境はGoogle Colab(ランタイムはCPU)です。 tとyは2乗和誤差と同じ値を入れています。

import numpy as np

# 以下のtとyに関して、2乗和誤差の時と同じ値
t = np.array([0, 1, 0, 1, 0]) #正解のデータ
y1 = np.array([0.01, 0.65, 0.13, 0.70, 0.15]) # 学習が上手くいっている場合
y2 = np.array([0.34, 0.21, 0.91, 0.33, 0.22 ]) # 学習が上手くいっていない場合

def cross_entropy_error(y, t):
    return -np.sum(t * np.log(y))

#交差エントロピー誤差の計算を実行
y1_loss = cross_entropy_error(y1, t)
y2_loss = cross_entropy_error(y2, t)

print(f"学習が上手くいっている時:{y1_loss}")
print(f"学習が上手くいっていない時: {y2_loss}")

Google Colab(ランタイムはCPU)で実行してみました。 計算結果は以下のようになりました。

学習が上手くいっている時:0.7874578600311867
学習が上手くいっていない時: 2.6693103727862795

上記のように、正解に近い値を推論結果の値で出力できている場合は、値が小さくなっているイメージが掴めたかと思います。

終わりに

いかがでしたでしょうか。今回は、損失関数の考え方や具体例を簡単にご紹介してみました。

機械学習の学習時に使うものなので「このモデルはどのようにして学習を進めているのか」という疑問を捉える時に見ると良い関数です。是非、ご参考にしてください。

本日はここまで。

明日は、活性化関数についてご紹介していきます。よければご覧ください!