RL强化学习入门(python都不会的那种)

首先需要python2.7的版本

导入gym库https://gym.openai.com/docs/

导入numpy库https://numpy.org/

""" Trains an agent with (stochastic) Policy Gradients on Pong. Uses OpenAI Gym. """
import numpy as np
import _pickle as pickle
import gym


# hyperparameters超参数
H = 200 # number of hidden layer neurons层数
batch_size = 10 # every how many episodes to do a param update?
learning_rate = 1e-4#学习率
gamma = 0.99 # discount factor for reward退火率
decay_rate = 0.99 # decay factor for RMSProp leaky sum of grad^2
resume = False # resume from previous checkpoint?
render = False

# model initialization
D = 80 * 80 # input dimensionality: 80x80 grid
if resume:
  model = pickle.load(open('save.p', 'rb'))#rb是用二进制方法读取二进制文件(非人工书写的数据如。jepg这种)
# open('文件路径', '模式',编码方式),
# #pickle.load是从文件中读取数据。
else:
  model = {}
  model['W1'] = np.random.randn(H,D) / np.sqrt(D) # "Xavier" 初始化(除以矩阵平方根即为转化为标准分布矩阵)
  #np.random.rand()函数作用;过本函数可以返回一个或一组服从“0~1”均匀分布的随机样本值。随机样本取值范围是[0,1),不包括1。
  #例如np.random.rand(3,3)就可以返回一组3行3列的随机矩阵,矩阵中每个元素的值为0-1(不包含1)
  #np.random.randn(d0,d1,d2……dn) ,当函数括号内没有参数时,则返回一个浮点数;当函数括号内有一个参数时,则返回秩为1的数组,不能表示向量和矩阵;
  #当函数括号内有两个及以上参数时,则返回对应维度的数组,能表示向量或矩阵;通过本函数可以返回一个或一组服从标准正态分布的随机样本值。
  model['W2'] = np.random.randn(H) / np.sqrt(H)#numpy.sqrt()为矩阵平方根,例如numpy.sqrt(【【1,4】,【9,16】】)输出为
  #【1,2】,【3,4】
  
grad_buffer = { k : np.zeros_like(v) for k,v in model.items() } # update buffers that add up gradients over a batch
rmsprop_cache = { k : np.zeros_like(v) for k,v in model.items() } # rmsprop memory
#np.zeros_like(v)的作用是构造一个矩阵并使其初始化为0.其shape与v矩阵相同
#for循环可以遍历任何序列的项目,如一个列表或者一个字符串EX;for letter in 'Python':print '当前字母 :', letter结果是p,y,t,h,o,n
#return的作用就是把你需要的东西拿过来def worker(a, b, c): x = a + b y = x * c return y 你让工人把y拿来他就会把x*c的结果拿给你。


def sigmoid(x): #定义 sigmoid这个函数
  return 1.0 / (1.0 + np.exp(-x)) # sigmoid "squashing" function to interval [0,1]

def prepro(I):
  """ prepro 210x160x3 uint8 frame into 6400 (80x80) 1D float vector """
  I = I[35:195] # crop裁剪这是numpy的切片操作,一般结构如num[a:b,c:d],分析时以逗号为分隔符,逗号之前为要取的num行的下标范围(a到b-1),
  #逗号之后为要取的num列的下标范围(c到d-1);此处为切片I矩阵的35行的195列
  I = I[::2,::2,0] # downsample by factor of 2按照步长2来采样
  #b = a[i:j:s]这种格式呢,i,j与上面的一样,但s表示步进,缺省为1上面的意思是行和列都按照步进2来
  #a[i:j:1]相当于a[i:j],当s<0时:i缺省时,默认为-1; j缺省时,默认为-len(a)-1
  #所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍。
  #len() 方法返回对象(字符、列表、元组等)长度或项目个数。

  I[I == 144] = 0 # erase background (background type 1)擦除背景
  #python中的‘==’ 是用来判断两个对象的值是否相等的,比如判断两个数字的值是否相等。判断的是内存块里存的值
  #I中=144的元素全部设置为0
  I[I == 109] = 0 # erase background (background type 2)擦除背景
  #I中=109的元素全部设置为0
  I[I != 0] = 1 # everything else (paddles, ball) just set to 1其他所有东西设置为1
  #!=代表不等于的意思,上式的意思是不等于0的元素全部设置为1
  return I.astype(np.float).ravel()
#astype(np.float)用来转换数据类型使其可以进行计算
#假设所取数据存在str型,那么取出的数据,全部转化为str型,也就是array阵列的元素全是str,不管数据库定义的是不是int型。
#那么问题来了,取出的数据代入公式进行计算的时候,就会类型不符,这是就用到astype(np.float)此时就可以转换为可计算的float类型

#ravel函数实现扁平化操作:一个矩阵ravel后会变成一行数组。ex:c = a.ravel() c为a的一种展示方式,虽然他们是不同的对象,但在修改c的时候,
#a中相应的数也改变了,所以扁平化时尽量用flatten函数。

def discount_rewards(r):#_单下划线开头:弱“内部使用”标识,如:”from M import *”,将不导入所有以下划线开头的对象,包括包、模块、成员
  """ take 1D float array of rewards and compute discounted reward """#取一维浮点奖励数组并计算折扣奖励
  discounted_r = np.zeros_like(r)
  running_add = 0
  for t in reversed(xrange(0, r.size)):#size返回矩阵元素的个数,axis = 0,返回该二维矩阵的行数,axis = 1,返回该二维矩阵的列数。
#xrange(stop)、xrange(start, stop[, step])。start: 计数从 start 开始。默认是从 0 开始。例如 xrange(5) 等价于 xrange(0, 5).
#stop: 计数到 stop 结束,但不包括 stop。例如:xrange(0, 5) 是 [0, 1, 2, 3, 4] 没有 5
#step:步长,默认为1。例如:xrange(0, 5) 等价于 xrange(0, 5, 1)
#EX;list(xrange(0,6,2)),[0, 2, 4]

#reversed()反转排序,可对列表、元组、区间等进行排序.a = [1,2,3,4,5,6,7,8,9,10],a_list = [x for x in reversed(a)]
#print(a_list),[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

    if r[t] != 0: running_add = 0 # reset the sum, since this was a game boundary (pong specific!)
#重置总和,因为这是一个游戏边界(特定于乒乓球!):如果r中元素不等于0那么设置的参数等于0

#!=是不等于的意思

#if 判断条件:执行语句、else:执行语句

    running_add = running_add * gamma + r[t]
    discounted_r[t] = running_add
  return discounted_r

def policy_forward(x):
  h = np.dot(model['W1'], x)
  #np.dot()为向量内积计算,计算过程就是将向量中对应元素相乘,再相加所得。即普通的向量乘法运算。
  h[h<0] = 0 # ReLU nonlinearity ReLU 非线性
  logp = np.dot(model['W2'], h)
  p = sigmoid(logp)
  return p, h # return probability of taking action 2, and hidden state 返回采取动作 2 的概率,以及隐藏状态

def policy_backward(eph, epdlogp):
  """ backward pass. (eph is array of intermediate hidden states) """#向后传递。(eph 是中间隐藏状态的数组)
  dW2 = np.dot(eph.T, epdlogp).ravel()#点乘后扁平化操作
  dh = np.outer(epdlogp, model['W2'])#np.outer()对于多维向量,全部展开变为一维向量
  dh[eph <= 0] = 0 # backpro prelu
  dW1 = np.dot(dh.T, epx)
  return {'W1':dW1, 'W2':dW2}

env = gym.make("Pong-v0")
observation = env.reset()
prev_x = None # used in computing the difference frame 用于计算差异
xs,hs,dlogps,drs = [],[],[],[]
running_reward = None
reward_sum = 0
episode_number = 0
while True:
  if render: env.render()

  # preprocess the observation, set input to network to be difference image 预处理,设置输入为不同的图像
  cur_x = prepro(observation)
  x = cur_x - prev_x if prev_x is not None else np.zeros(D)
  prev_x = cur_x

  # forward the policy network and sample an action from the returned probability 更新网络策略并且从返回的概率中采样一个动作
  aprob, h = policy_forward(x)
  action = 2 if np.random.uniform() < aprob else 3 # roll the dice!掷骰子
  #np.random()创建一个随机矩阵,里面的元素取值范围是[0, 1)

  #np.random.uniform()生成一个矩阵里面的元素在(x,y)范围内随机取值

  # record various intermediates (needed later for backprop)记录各种中间层(稍后需要用于反向传播)
  xs.append(x) # observation观察

  #append()向列表尾部加一个新元素

  hs.append(h) # hidden state隐藏层
  y = 1 if action == 2 else 0 # a "fake label"“假标签
  dlogps.append(y - aprob) # grad that encourages the action that was taken to be taken (see http://cs231n.github.io/neural-networks-2/#losses if confused)
  #鼓励采取行动

  # step the environment and get new measurements更新环境并获得新的测量值
  observation, reward, done, info = env.step(action)#将环境动作赋值给左边的变量
  reward_sum += reward#c+=a等同于c=c+a

  drs.append(reward) # record reward (has to be done after we call step() to get reward for previous action)
  #记录奖励(必须在我们调用 step() 以获得之前操作的奖励之后完成)

  if done: # an episode finished 完成了一步
    episode_number += 1

    # stack together all inputs, hidden states, action gradients, and rewards for this episode
    #将这一集的所有输入、隐藏状态、动作梯度和奖励叠加在一起
    epx = np.vstack(xs)#np.vstack()把数组垂直(按照行顺序排列)堆叠起来EX:a=[[1],[2],[3]],b=[[1],[2],[3]]
    #输出会变成
    #[[1]
    #[2]
    #[3]
    #[1]
    #[2]
    #[3]

    eph = np.vstack(hs)
    epdlogp = np.vstack(dlogps)
    epr = np.vstack(drs)
    xs,hs,dlogps,drs = [],[],[],[] # reset array memory重置矩阵内存

    # compute the discounted reward backwards through time 向后计算折扣奖励
    discounted_epr = discount_rewards(epr)
    # standardize the rewards to be unit normal (helps control the gradient estimator variance)
    #将奖励标准化为单位正态(有助于控制梯度估计方差)
    discounted_epr -= np.mean(discounted_epr)#np.mean()对指定的行或者列或者所有元素求均值
    discounted_epr /= np.std(discounted_epr)#np.std()计算标准差

    epdlogp *= discounted_epr # modulate the gradient with advantage (PG magic happens right here.)
    #利用优势调整梯度(PG 魔法就在这里)
    grad = policy_backward(eph, epdlogp)
    for k in model: grad_buffer[k] += grad[k] # accumulate grad over batch 按批次累加梯度

    # perform rmsprop parameter update every batch_size episodes 每一次batch_size时就更新rmsprop参数
    if episode_number % batch_size == 0:
    #%是返回除法的余数EX:b % a 输出结果 1(a=10,b=21)
      for k,v in model.items():
        g = grad_buffer[k] # gradient梯度
        rmsprop_cache[k] = decay_rate * rmsprop_cache[k] + (1 - decay_rate) * g**2
        model[k] += learning_rate * g / (np.sqrt(rmsprop_cache[k]) + 1e-5)
        grad_buffer[k] = np.zeros_like(v) # reset batch gradient buffer重新按批次设置梯度

    # boring book-keeping 没什么用的一些记账
    running_reward = reward_sum if running_reward is None else running_reward * 0.99 + reward_sum * 0.01
    print ('resetting env. episode reward total was %f. running mean: %f') % (reward_sum, running_reward)
    if episode_number % 100 == 0: pickle.dump(model, open('save.p', 'wb'))
    #pickle.dump()将序列存储到文件中
    #EX;pickle.dump(obj, file, protocol)、obj——序列化对象,将对象obj保存到文件file中去;
    #file——file表示保存到的类文件对象,file必须有write()接口,file可以是一个以’w’打开的文件或者是一个StringIO对象,也可以是任何可以实现write()接口的对象;
    #protocol——序列化模式,默认是 0(ASCII协议,表示以文本的形式进行序列化),protocol的值还可以是1和2(1和2表示以二进制的形式进行序列化。其中,1是老式的二进制协议;2是新二进制协议)。
    reward_sum = 0
    observation = env.reset() # reset env重置环境
    prev_x = None

  if reward != 0: # Pong has either +1 or -1 reward exactly when game ends.在游戏结束时有 +1 或 -1 的奖励。
    print ('ep %d: game finished, reward: %f' % (episode_number, reward)) + ('' if reward == -1 else ' !!!!!!!!')

引用自http://karpathy.github.io/2016/05/31/rl/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值