Redundanz

僕の言葉は、人と話をするためにあるんじゃない。

0322

 昨日はバイト先から「一問解答を作成するごとに二千円」という案件が来ていて4問ほど解いてみたのですが全然割に合いませんでした。まあ時給にするとそれなりだけど、QOL換算で。多分僕は仕事と私事を分けるのが下手なタイプなのだと思います。自宅で作業するとそれにのめり込んでしまって自分の時間が消滅してしまう。ある意味でそれは長所なのだけれど、それほど優先度の高くない課題に対してもそうなってしまうということは、単に優先順位をつけられていないってことなのでしょう。

 今日はQ学習を実装してみました。ひとまず最初はマルバツゲームをやらせてみることに。Q関数をどう設定してやれば良いのかわからなかったので適当にニューラルネットで表現してみることにしました。盤面の現状を入力にとり、次の一手となりうる9手それぞれのQ値を出力とするようなパーセプトロンを用意して、バックプロパゲーションで学習。ランダムに打ってくる敵と一万回くらい対戦させたところまず負けなくなりました。良い感じです。ただマルバツゲームはちょっと分り易すぎた感があるので、今度はもうちょっともやもやした課題をやらせてみよう。

 一応コードも貼っておく。ニューラルネットの部分はこちら*1を借用してちょっと改変しただけ。

import numpy as np
def tanh(x):
    return np.tanh(x)
def tanh_deriv(x):
    return 1.0 - x**2
def logistic(x):
    return 1/(1 + np.exp(-x))
def logistic_derivative(x):
    return logistic(x)*(1-logistic(x))

class NeuralNetwork:
    def __init__(self, layers, activation='tanh'):
        """
        :param layers: A list containing the number of units in each layer.
        Should be at least two values
        :param activation: The activation function to be used. Can be
        "logistic" or "tanh"
        """
        if activation == 'logistic':
            self.activation = logistic
            self.activation_deriv = logistic_derivative
        elif activation == 'tanh':
            self.activation = tanh
            self.activation_deriv = tanh_deriv

        self.weights = []
        for i in range(0, len(layers) - 1):
#            self.weights.append((2*np.random.random((layers[i - 1] , layers[i]
#                                ))-1)*0.25)
            self.weights.append((2*np.random.random((layers[i], layers[i +
                                1]))-1)*0.25)
            
    def fit(self, X, y, learning_rate=0.1, epochs=10000):
        X = np.atleast_2d(X)
        temp = np.ones([X.shape[0], X.shape[1]+1])
        temp[:, 0:-1] = X  # adding the bias unit to the input layer
        X = temp
        y = np.array(y)

        for k in range(epochs):
            i = np.random.randint(X.shape[0])
            a = [X[i]]

            for l in range(0,len(self.weights)):
                a.append(self.activation(np.dot(a[l], self.weights[l])))
            error = y[i] - a[-1]
            deltas = [error * self.activation_deriv(a[-1])]

            for l in range(len(a) - 2, 0, -1): # we need to begin at the second to last layer
                deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_deriv(a[l]))
            deltas.reverse()
            for i in range(len(self.weights)):
                layer = np.atleast_2d(a[i])
                delta = np.atleast_2d(deltas[i])
                self.weights[i] += learning_rate * layer.T.dot(delta)
                
    def fit_once(self, x, y, learning_rate=0.2):
        x = np.array(x).flatten()
        x = np.insert(x, len(x), 1)
        y = np.array(y)
        
        a = [x]
        
        for l in range(len(self.weights)):
            a.append(self.activation(np.dot(a[l],self.weights[l])))
        error = y - a[-1]
        deltas = [error * self.activation_deriv(a[-1])]
            
        for l in range(len(a)-2, 0, -1):
            deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_deriv(a[l]))
        deltas.reverse()
        for i in range(len(self.weights)):
            layer = np.atleast_2d(a[i])
            delta = np.atleast_2d(deltas[i])
            self.weights[i] += learning_rate * layer.T.dot(delta)

    def predict(self, x):
        x = np.array(x)
        temp = np.ones(x.shape[0]+1)
        temp[0:-1] = x
        a = temp
        for l in range(0, len(self.weights)):
            a = self.activation(np.dot(a, self.weights[l]))
        return a

def judge(board):
    winner = 0
    pos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
    for p in pos:
        if board[p[0]] == board[p[1]] and board[p[1]] == board[p[2]] and board[p[0]]!=0:
            winner = board[p[0]]
    return winner

def Q_learn(net):
    eta = 0.1
    gamma = 0.95
    board = [0,0,0,0,0,0,0,0,0]
    while judge(board)==0 and reduce(lambda x,y:x*y,board)==0:
        Q_list = net.predict(board)
        target=[]
        next_move_Q=-1
        next_move = 0
        for i in range(9):
            if board[i] != 0:
                target.append(-1)
                continue
            board_n = list(board)
            board_n[i] = 1
            R = judge(board_n)
            maxQ_n = max(net.predict(board_n)) if R!=1 else 1
            Q_i = Q_list[i]
            if Q_i > next_move_Q:
                next_move_Q = Q_i
                next_move = i
            target.append( R + gamma*maxQ_n - Q_i )
        y = np.array(Q_list) + np.array(target) * eta
        net.fit_once(board,y)
        
        board[next_move] = 1
        switch = 0
        while switch==0 and judge(board)==0 and reduce(lambda x,y:x*y,board)==0:
            enemy = np.random.randint(9)
            if board[enemy]==0:
                board[enemy]=-1
                switch = 1
        #print board
        
    return judge(board)

実験。

net = NeuralNetwork([10,20,9])

rate = []
for i in range(100):
    win = 0
    for j in range(100):
        if Q_learn(net)==1:
            win += 1
    rate.append(win/(100.0))

結果。
100回勝負を一セットとして100セットやった勝率の推移。
f:id:Raprto:20150323005104p:plain
たぶん僕より強い。