المقدمة
ذات مرة ، أثناء قراءة كتاب "التعلم المعزز: مقدمة" ، فكرت في استكمال معرفتي النظرية بمعرفة عملية ، لكن لم تكن هناك رغبة في حل المشكلة التالية المتمثلة في موازنة العارضة ، أو تعليم وكيل لعب الشطرنج أو اختراع دراجة أخرى.
في الوقت نفسه ، احتوى الكتاب على مثال واحد مثير للاهتمام لتحسين قائمة انتظار العملاء ، والتي ، من ناحية ، ليست معقدة للغاية من حيث تنفيذ / فهم العملية ، ومن ناحية أخرى ، فهي ممتعة للغاية ويمكن تنفيذها ببعض النجاح في الحياة الواقعية.
بعد أن غيرت هذا المثال قليلاً ، توصلت إلى الفكرة ، والتي ستتم مناقشتها أكثر.
صياغة المشكلة
تخيل الصورة التالية:
لدينا مخبز ينتج 6 (بشروط) من فطائر التوت كل يوم ويوزع هذه المنتجات على ثلاثة متاجر كل يوم.
ومع ذلك ، ما هي أفضل طريقة للقيام بذلك بحيث يكون هناك أقل عدد ممكن من المنتجات منتهية الصلاحية (بشرط أن تكون مدة صلاحية الفطائر ثلاثة أيام) ، إذا كان لدينا ثلاث شاحنات فقط بسعة 1 و 2 و 3 أطنان ، على التوالي ، في كل نقطة بيع يكون الأمر الأكثر ربحية لإرسال شاحنة واحدة فقط (لأنها تقع على مسافة كافية من بعضها البعض) ، وعلاوة على ذلك ، مرة واحدة فقط في اليوم بعد خبز الفطائر ، وإلى جانب ذلك ، لا نعرف القوة الشرائية في متاجرنا (منذ أن بدأ العمل للتو)؟
دعنا نتفق على أن استراتيجية تخطيط FIFO تعمل بشكل مثالي في المتاجر ، حيث يأخذ العملاء فقط البضائع التي تم إنتاجها في وقت متأخر عن الآخرين ، ولكن إذا لم يتم شراء فطيرة التوت في غضون ثلاثة أيام ، يتخلص موظفو المتجر منها.
نحن (بشروط) لا نعرف ما هو الطلب على الفطائر في يوم معين في متجر معين ، ولكن في المحاكاة التي قمنا بها ، قمنا بتعيينها على النحو التالي لكل من المتاجر الثلاثة: 3 ± 0.1 ، 1 ± 0.1 ، 2 ± 0.1.
من الواضح أن الخيار الأكثر ربحية بالنسبة لنا هو إرسال ثلاثة أطنان إلى المتجر الأول ، وواحد إلى الثاني ، وطنان من الفطائر إلى المتجر الثالث ، على التوالي.
لحل هذه المشكلة ، نستخدم بيئة رياضية مخصصة ، بالإضافة إلى Deep Q Learning (تنفيذ Keras).
بيئة مخصصة
سنصف حالة البيئة بثلاثة أرقام موجبة حقيقية - باقي المنتجات لليوم الحالي في كل من المتاجر الثلاثة. إجراءات الوكيل عبارة عن أرقام من 0 إلى 5 شاملة ، تدل على مؤشرات تبديل الأعداد الصحيحة 1 و 2 و 3. ومن الواضح أن الإجراء الأكثر فائدة سيكون تحت الفهرس الرابع (3 ، 1 ، 2). نعتبر المهمة عرضية ، في حلقة واحدة 30 يومًا.
import gym
from gym import error, spaces, utils
from gym.utils import seeding
import itertools
import random
import time
class ShopsEnv(gym.Env):
metadata = {'render.modes': ['human']}
# ,
#
def __init__(self):
self.state = [0, 0, 0] #
self.next_state = [0, 0, 0] #
self.done = False #
self.actions = list(itertools.permutations([1, 2, 3])) #
self.reward = 0 #
self.time_tracker = 0 #
self.remembered_states = [] #
#
t = int( time.time() * 1000.0 )
random.seed( ((t & 0xff000000) >> 24) +
((t & 0x00ff0000) >> 8) +
((t & 0x0000ff00) << 8) +
((t & 0x000000ff) << 24) )
# ()
def step(self, action_num):
#
if self.done:
return [self.state, self.reward, self.done, self.next_state]
else:
#
self.state = self.next_state
#
self.remembered_states.append(self.state)
#
self.time_tracker += 1
#
action = self.actions[action_num]
# , ( )
self.next_state = [x + y for x, y in zip(action, self.state)]
#
self.next_state[0] -= (3 + random.uniform(-0.1, 0.1))
self.next_state[1] -= (1 + random.uniform(-0.1, 0.1))
self.next_state[2] -= (2 + random.uniform(-0.1, 0.1))
#
if any([x < 0 for x in self.next_state]):
self.reward = sum([x for x in self.next_state if x < 0])
else:
self.reward = 1
#
#
# ( ),
#
if self.time_tracker >= 3:
remembered_state = self.remembered_states.pop(0)
self.next_state = [max(x - y, 0) for x, y in zip(self.next_state, remembered_state)]
else:
self.next_state = [max(x, 0) for x in self.next_state]
# 30
self.done = self.time_tracker == 30
#
return [self.state, self.reward, self.done, self.next_state]
#
def reset(self):
#
self.state = [0, 0, 0]
self.next_state = [0, 0, 0]
self.done = False
self.reward = 0
self.time_tracker = 0
self.remembered_states = []
t = int( time.time() * 1000.0 )
random.seed( ((t & 0xff000000) >> 24) +
((t & 0x00ff0000) >> 8) +
((t & 0x0000ff00) << 8) +
((t & 0x000000ff) << 24) )
#
return self.state
# :
#
def render(self, mode='human', close=False):
print('-'*20)
print('First shop')
print('Pies:', self.state[0])
print('Second shop')
print('Pies:', self.state[1])
print('Third shop')
print('Pies:', self.state[2])
print('-'*20)
print('')
الواردات الرئيسية
import numpy as np #
import pandas as pd #
import gym #
import gym_shops #
from tqdm import tqdm #
#
import matplotlib.pyplot as plt
import seaborn as sns
from IPython.display import clear_output
sns.set_color_codes()
#
from collections import deque
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam
import random #
تحديد الوكيل
class DQLAgent():
def __init__(self, env):
#
self.state_size = 3 #
self.action_size = 6 #
# replay()
self.gamma = 0.99
self.learning_rate = 0.01
# adaptiveEGreedy()
self.epsilon = 0.99
self.epsilon_decay = 0.99
self.epsilon_min = 0.0001
self.memory = deque(maxlen = 5000) # 5000 , -
# (NN)
self.model = self.build_model()
# Deep Q Learning
def build_model(self):
model = Sequential()
model.add(Dense(10, input_dim = self.state_size, activation = 'sigmoid')) #
model.add(Dense(50, activation = 'sigmoid')) #
model.add(Dense(10, activation = 'sigmoid')) #
model.add(Dense(self.action_size, activation = 'sigmoid')) #
model.compile(loss = 'mse', optimizer = Adam(lr = self.learning_rate))
return model
#
def remember(self, state, action, reward, next_state, done):
self.memory.append((state, action, reward, next_state, done))
#
def act(self, state):
# 0 1 epsilon
# (exploration)
if random.uniform(0,1) <= self.epsilon:
return random.choice(range(6))
else:
#
act_values = self.model.predict(state)
return np.argmax(act_values[0])
#
def replay(self, batch_size):
# ,
if len(self.memory) < batch_size:
return
minibatch = random.sample(self.memory, batch_size) # batch_size
#
for state, action, reward, next_state, done in minibatch:
if done: # -
target = reward
else:
#
target = reward + self.gamma * np.amax(self.model.predict(next_state)[0])
# target = R(s,a) + gamma * max Q`(s`,a`)
# target (max Q` value) , s`
train_target = self.model.predict(state) # s --> NN --> Q(s,a) = train_target
train_target[0][action] = target
self.model.fit(state, train_target, verbose = 0)
# exploration rate,
# epsilon
def adaptiveEGreedy(self):
if self.epsilon > self.epsilon_min:
self.epsilon *= self.epsilon_decay
تدريب الوكيل
# gym
env = gym.make('shops-v0')
agent = DQLAgent(env)
#
batch_size = 100
episodes = 1000
#
progress_bar = tqdm(range(episodes), position=0, leave=True)
for e in progress_bar:
#
state = env.reset()
state = np.reshape(state, [1, 3])
# , id
time = 0
taken_actions = []
sum_rewards = 0
#
while True:
#
action = agent.act(state)
#
taken_actions.append(action)
#
next_state, reward, done, _ = env.step(action)
next_state = np.reshape(next_state, [1, 3])
#
sum_rewards += reward
#
agent.remember(state, action, reward, next_state, done)
#
state = next_state
# replay
agent.replay(batch_size)
# epsilon
agent.adaptiveEGreedy()
#
time += 1
#
progress_bar.set_postfix_str(s='mean reward: {}, time: {}, epsilon: {}'.format(round(sum_rewards/time, 3), time, round(agent.epsilon, 3)), refresh=True)
#
if done:
#
clear_output(wait=True)
sns.distplot(taken_actions, color="y")
plt.title('Episode: ' + str(e))
plt.xlabel('Action number')
plt.ylabel('Occurrence in %')
plt.show()
break
اختبار الوكيل
import time
trained_model = agent #
state = env.reset() #
state = np.reshape(state, [1,3])
#
time_t = 0
MAX_EPISOD_LENGTH = 1000 #
taken_actions = []
mean_reward = 0
#
progress_bar = tqdm(range(MAX_EPISOD_LENGTH), position=0, leave=True)
for time_t in progress_bar:
#
action = trained_model.act(state)
next_state, reward, done, _ = env.step(action)
next_state = np.reshape(next_state, [1,3])
state = next_state
taken_actions.append(action)
#
clear_output(wait=True)
env.render()
progress_bar.set_postfix_str(s='time: {}'.format(time_t), refresh=True)
print('Reward:', round(env.reward, 3))
time.sleep(0.5)
mean_reward += env.reward
if done:
break
#
sns.distplot(taken_actions, color='y')
plt.title('Test episode - mean reward: ' + str(round(mean_reward/(time_t+1), 3)))
plt.xlabel('Action number')
plt.ylabel('Occurrence in %')
plt.show()
مجموع
وهكذا ، فهم الوكيل بسرعة كيف يتصرف بأكبر قدر من الربحية.
بشكل عام ، لا يزال هناك مجال كبير للتجربة: يمكنك زيادة عدد المتاجر ، أو تنويع الإجراءات ، أو حتى مجرد تغيير المعلمات الفائقة لنموذج التدريب - وهذه مجرد بداية القائمة.