[PYTHON] Jouer avec l'apprentissage automatique: Q-Learning peut-il déterminer si des actions liées au marketing doivent être prises?

introduction

Précédemment Pouvez-vous apprendre des jeux supplémentaires avec un apprentissage amélioré? a été essayé et j'ai pu apprendre sans aucun problème.

Cette fois, je suppose un problème plus réaliste.

Il ya un problème. Eh bien, il y a une histoire que vous devriez envoyer à tout le monde s'il s'agit de courrier électronique, mais si vous en envoyez trop, cela entraînera un retrait, et les coupons sont coûteux, donc je ne veux pas éclater.

La question cette fois est de savoir ce qui se passerait si ce problème était résolu dans un cadre Q-Learning. Avec Q-Learning, c'est un bon endroit pour pouvoir gérer plusieurs actions. Cependant, pensez-y comme une situation simple et complètement virtuelle.

Thème: Pouvez-vous décider d'entreprendre des actions liées au marketing avec Q-Learning? Jeu

Jeu

Pour l'écrire facilement

C'est comme ça.

Pourquoi avez-vous pensé à un tel jeu

Dans le cas de ce jeu, je craignais qu'il ne soit difficile de dire qu'il y a un intervalle entre la réalisation de la meilleure action et l'obtention de la récompense. Il semble que l'apprentissage sera rapide si vous obtenez une récompense immédiatement après l'action optimale, mais le plus important est que ce n'est pas le cas. Je pense qu'il est difficile de définir le terme «pénalité», mais maintenant je ne fais que le donner à des actions qui semblent être erronées. (Est-ce trop facile pour le problème ...?).

Cependant, comme vous pouvez le voir dans Explorer le labyrinthe avec un apprentissage amélioré, vous pouvez apprendre rétroactivement, donc je me demande si vous pouvez le faire. Je voulais juste le vérifier.

résultat

Vous n'obtiendrez des récompenses que lorsque U = 2 se produit, augmentez donc chance_count lorsque U = 2 se produit. La récompense maximale que vous pouvez obtenir sur une période donnée est «chance_count». Par conséquent, laissez hit_rate être la récompense / chance_count obtenue. La figure ci-dessous montre comment cela a changé après 10 000 apprentissages / évaluations.

スクリーンショット_2015_12_31_11_50.png

J'ai essayé environ 50 millions de fois, mais après environ 30 millions de fois, j'ai senti que l'apprentissage avait atteint son apogée et qu'il s'agissait d'environ hit_rate = 0,9.

Considération

à la fin

Q L'apprentissage est amusant car cela ressemble à une simple IA en un sens. J'espère qu'il peut être utilisé pour quelque chose.

code

Je le posterai pour référence.

#!/usr/bin/env python
# coding: utf-8

import numpy as np
from random import random, choice


class Game(object):
    state = None
    actions = None
    game_over = False

    def __init__(self, player):
        self.player = player
        self.turn = 0
        self.last_reward = 0
        self.total_reward = 0
        self.init_state()

    def player_action(self):
        action = self.player.action(self.state, self.last_reward)
        if action not in self.actions:
            raise Exception("Invalid Action: '%s'" % action)
        self.state, self.last_reward = self.get_next_state_and_reward(self.state, action)

    def play(self):
        yield(self)
        while not self.game_over:
            self.player_action()
            self.turn += 1
            self.total_reward += self.last_reward
            yield(self)

    def init_state(self):
        raise NotImplemented()

    def get_next_state_and_reward(self, state, action):
        raise NotImplemented()


class UserAndPushEventGame(Game):
    """
    State           :S : list of (U, A)
    UserActivity    :U : int of 0~3
    Action          :A : int of 0 or 1

    Next-State(S, A):S':
        S[-1][1] = A
        S.append((Next-U, None))
        S = S[-5:]
    Next-U          :  :
        if S[-4] == (2, 1) then 3
        else 10% -> 2, 10% -> 1, 80% -> 0
    Reward(S, A)    :R :
        if S[-1] == (3, *) then R += 1
        wrong_action_count := Number of ({0,1,3}, 1) in S
        R -= wrong_action_count * 0.3
    """

    STATE_HISTORY_SIZE = 5

    def init_state(self):
        self.actions = [0, 1]
        self.state = [(0, None)]
        self.chance_count = 0

    def get_next_state_and_reward(self, state, action):
        next_state = (state + [(self.next_user_action(state), None)])[-self.STATE_HISTORY_SIZE:]
        next_state[-2] = (next_state[-2][0], action)
        reward = 0
        if len(state) > 0 and state[-1][0] == 3:
            reward += 1
        action_count = reduce(lambda t, x: t+(x[1] or 0), state, 0)
        correct_action_count = len([0 for x in state if x == (2, 1)])
        wrong_action_count = action_count - correct_action_count
        reward -= wrong_action_count * 0.3
        return next_state, reward

    def next_user_action(self, state):
        if len(state) > 4 and state[-4] == (2, 1):
            return 3
        else:
            rnd = np.random.random()
            if rnd < 0.8:
                return 0
            elif rnd < 0.9:
                return 1
            else:
                self.chance_count += 1
                return 2


class HumanPlayer(object):
    training = False

    def action(self, state, last_reward):
        print "LastReward=%s, CurrentState: %s" % (last_reward, state)
        while True:
            action_input = raw_input("Enter 0~1: ")
            if int(action_input) in [0, 1]:
                return int(action_input)


class QLearnPlayer(object):
    ALPHA = 0.1
    GAMMA = 0.99
    E_GREEDY = 0.05

    def __init__(self):
        self.actions = [0, 1]
        self.q_table = {}
        self.last_state = self.last_action = None
        self.training = True

    def get_q_value(self, state, action):
        return self.q_table.get(state, {}).get(action, (np.random.random() - 0.5)/1000)  #Undefined renvoie un petit nombre aléatoire

    def get_all_q_values(self, state):
        return [self.get_q_value(state, act) for act in self.actions]

    def set_q_value(self, state, action, val):
        if state in self.q_table:
            self.q_table[state][action] = val
        else:
            self.q_table[state] = {action: val}

    def action(self, state, last_reward):
        state = tuple(state)
        next_action = self.select_action(state)
        if self.last_state is not None:
            self.update_q_table(self.last_state, self.last_action, state, last_reward)
        self.last_state = state
        self.last_action = next_action
        return next_action

    def select_action(self, state):
        if self.training and random() < self.E_GREEDY:
            return choice(self.actions)
        else:
            return np.argmax(self.get_all_q_values(state))

    def update_q_table(self, last_state, last_action, cur_state, last_reward):
        if self.training:
            d = last_reward + np.max(self.get_all_q_values(cur_state)) * self.GAMMA - self.get_q_value(last_state, last_action)
            self.set_q_value(last_state, last_action, self.get_q_value(last_state, last_action) + self.ALPHA * d)


if __name__ == '__main__':
    SWITCH_MODE_TURN_NUM = 10000
    fp = file("result.txt", "w")
    dt = file("detail.txt", "w")
    player = QLearnPlayer()
    # player = HumanPlayer()
    game = UserAndPushEventGame(player)
    last_chance_count = last_score = 0
    for g in game.play():
        # dt.write("%s: isT?=%s LastReward=%s TotalReward=%s S=%s\n" %
        #          (g.turn, player.training, g.last_reward, g.total_reward, g.state))
        if g.turn % SWITCH_MODE_TURN_NUM == 0:
            if not player.training:
                this_term_score = game.total_reward - last_score
                this_term_chance = game.chance_count - last_chance_count
                if this_term_chance > 0:
                    hit_rate = 100.0*this_term_score/this_term_chance
                else:
                    hit_rate = 0
                # print "Turn=%d: This 100 turn score=%2.2f chance=%02d: HitRate=%.1f%% %s" % \
                #       (g.turn, this_term_score, this_term_chance, hit_rate, '*' * int(hit_rate/2))
                fp.write("%d\t%.2f\t%d\t%f\n" % (g.turn, this_term_score, this_term_chance, hit_rate))
            last_score = game.total_reward
            last_chance_count = game.chance_count
            player.training = not player.training
        if g.turn % 10000 == 0:
            fp.flush()

Recommended Posts

Jouer avec l'apprentissage automatique: Q-Learning peut-il déterminer si des actions liées au marketing doivent être prises?
L'apprentissage automatique peut-il prédire les quadrilatères parallèles? (1) Peut-il être inséré à l'extérieur?
pd.tseries.offsets.DateOffset peut être assez lent s'il n'est pas utilisé avec prudence
Déterminez si la chaîne est formatable
Assurez-vous que python peut convertir / convertir une chaîne de caractères en int
[Python] Arrondissez avec juste l'opérateur
Jouer avec l'apprentissage automatique: Q-Learning peut-il déterminer si des actions liées au marketing doivent être prises?
Comment entraîner Kaldi avec JUST Corpus
Modifiez la chaîne de caractères à remplacer en fonction de la chaîne de caractères correspondante en la remplaçant par l'expression régulière Python
Comment supprimer la chaîne de caractères spécifiée avec la commande sed! !! !!
Ajoutez simplement le pilote à la touche de forme avec mélangeur