訓練データ(xn, yn), xn ∈ R D, yn ∈ {1,2,...C}, n=1,2,...,Nを使って、マルチクラス識別問題を解くモデルを考えます。
多項ロジットのモデル
多項ロジットでは以下のモデルを使います。
分子は訓練データxの特徴量の線形結合をロジスティック関数に入れたものです。
分子は非負の値を取ります。
上の式はxがクラスyに属する確率を表します。
よって、xがクラス1に属する確率、xがクラス2に属する確率、...をすべて足すと1.0になっていて欲しいです。この条件を満たすように分母の値で割って正規化を行います。
パラメータの学習
多項ロジットのパラメータは、
つまりクラスの数だけ重みベクトルが存在します。
これらのパラメータを学習するために、以下の対数尤度関数を最大化します。
対数尤度関数の偏微分を計算します。
まず第一項の偏微分は以下のようになります。
第二項の偏微分は以下のようになります。
よって全体の偏微分は、
となります。
偏微分が求まったので、θを適当な値に初期化して、勾配法で対数尤度関数を最適化することができます。
実装
Pythonで実装しました。
import numpy as np class MultinomialLogit: def __init__(self): self.C = 0 self.N = 0 self.D = 0 self.theta = None def prob(self, X, y): return np.dot(self.theta[y], X) / sum(np.dot(self.theta[c], X) for c in range(self.C)) def logLiklihood(self, X, y): return sum(np.log(self.prob(X[n], y[n])) for n in range(self.N)) def fit(self, X, y, iter=200): self.C = len(set(y)) self.N = len(X) self.D = len(X[0])+1 # bias term is added self.theta = np.random.rand(self.C, self.D); bias = np.ones((len(X), 1)) X = np.hstack((X, bias)) def delta(a, b): return 1.0 if a == b else 0.0 for i in range(iter): for c in range(self.C): gradient = sum((delta(c, y[n]) - self.prob(X[n], c)) * X[n] for n in range(self.N)) self.theta[c] += gradient print ("i=%d %f" % (i, self.logLiklihood(X, y))) def predict(self, X): bias = np.ones((len(X), 1)) X = np.hstack((X, bias)) return [np.argmax([self.prob(x, c) for c in range(self.C)]) for x in X] def score(self, X, y): Ypred = self.predict(X) comp = Ypred == y return sum(comp) / len(comp)
Irisのデータを使って実験してみました。
テスト識別率は80%後半程度です。sklearnのLogisticRegressionだと100%が出るので全然ダメな感じですが、
- iterationごとに対数尤度関数が上昇すること
- 対数尤度関数が上がるとテスト識別率も上がること
を確認できたのでそれっぽい実装は出来てそうです。学習率を設定したり、 ヘッセ行列を計算してニュートン法系の最適化を行ったり、確率的勾配法を使ったりなど改善の余地はたくさんありそうです。
if __name__ == '__main__': from sklearn import datasets from sklearn import cross_validation iris = datasets.load_iris() X = iris.data y = iris.target Xtrain, Xtest, Ytrain, Ytest = cross_validation.train_test_split(X, y, test_size=0.3) logit = MultinomialLogit() logit.fit(Xtrain, Ytrain, iter=1000) print (logit.score(Xtest, Ytest))
0 件のコメント:
コメントを投稿