TensorFlow 深度学习第二版:6~10(5)

简介: TensorFlow 深度学习第二版:6~10(5)

TensorFlow 深度学习第二版:6~10(4)https://developer.aliyun.com/article/1426805

以下实现基于 TensorFlow:

首先,我们需要导入所有库:

import gym
import numpy as np
import random
import tensorflow as tf
import matplotlib.pyplot as plt

然后我们加载并设置环境以进行测试:

env = gym.make('FrozenLake-v0')

输入网络是一种状态,以张量形状[1,16]编码。因此,我们定义了input1占位符:

inputs1 = tf.placeholder(shape=[1,16],dtype=tf.float32)

网络权重最初由tf.random_uniform函数随机选择:

W = tf.Variable(tf.random_uniform([16,4],0,0.01))

网络输出由inputs1占位符和权重的乘积给出:

Qout = tf.matmul(inputs1,W)

Qout上计算的argmax将给出预测值:

predict = tf.argmax(Qout,1)

最佳动作(nextQ)以张量形状编码[1,4]:

nextQ = tf.placeholder(shape=[1,4],dtype=tf.float32)

接下来,我们定义一个损失函数来实现反向传播过程。

损失函数是loss = ∑(Q - target - Q),其中计算当前预测的 Q 值和目标值之间的差异,并且梯度通过网络传递:

loss = tf.reduce_sum(tf.square(nextQ - Qout))

优化函数是众所周知的GradientDescentOptimizer

trainer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
updateModel = trainer.minimize(loss)

重置并初始化计算图:

tf.reset_default_graph()
init = tf.global_variables_initializer()

然后我们设置 Q-Learning 训练过程的参数:

y = .99
e = 0.1
num_episodes = 6000
jList = []
rList = []

我们定义会话sess,其中网络必须学习最佳的移动顺序:

with tf.Session() as sess:
    sess.run(init)
    for i in range(num_episodes):
        s = env.reset()
        rAll = 0
        d = False
        j = 0
        while j < 99:
            j+=1

输入状态用于为网络提供信息:

a,allQ = sess.run([predict,Qout],\
                              feed_dict=\
                              {inputs1:np.identity(16)[s:s+1]})

从输出张量a中选择一个随机状态:

if np.random.rand(1) < e:
                a[0] = env.action_space.sample()

使用env.step()函数执行a[0]动作,获得奖励,r和状态,s1

s1,r,d,_ = env.step(a[0])

新状态s1用于更新Q张量:

Q1 = sess.run(Qout,feed_dict=\
                          {inputs1:np.identity(16)[s1:s1+1]})
            maxQ1 = np.max(Q1)
            targetQ = allQ
            targetQ[0,a[0]] = r + y*maxQ1

当然,必须为反向传播过程更新权重:

_,W1 = sess.run([updateModel,W],\
                             feed_dict=\
                           {inputs1:np.identity(16)[s:s+1],nextQ:targetQ})

rAll这里定义了会话期间获得的总奖励。让我们回想一下,RL 智能体的目标是最大化它从长远来看所获得的总奖励:

rAll += r

更新下一步的环境状态:

s = s1
           if d == True:
                e = 1./((i/50) + 10)
                break
   jList.append(j)
   rList.append(rAll)

计算结束时,将显示成功剧集的百分比:

print ("Percent of successfulepisodes: " +\
str(sum(rList)/num_episodes) + "%")

如果我们运行模型,我们应该得到这样的结果,可以通过调整网络参数来改进:

>>>[2017-01-15 16:56:01,048] Making new env: FrozenLake-v0
Percentage of successful episodes: 0.558%

深度 Q 学习

多亏了 DeepMind 在 2013 年和 2016 年取得的近期成就,它成功地在 Atari 游戏中达到了所谓的超人级别,并击败了围棋世界冠军,RL 在机器学习社区中变得非常有趣。这种新的兴趣也是由于深度神经网络(DNN)表现为近似函数,使这种算法的潜在价值达到了更高的水平。最近获得最多兴趣的算法肯定是深度 Q-Learning。以下部分介绍深度 Q-Learning 算法,并讨论一些优化技术以最大化其表现。

深度 Q 神经网络

当状态数和可能的动作增加并且从矩阵的角度来看变得难以管理时,Q 学习基础算法会引起巨大的问题。想想谷歌使用的结构输入配置,以达到 Atari 游戏的表现水平。状态空间是离散的,但国家的数量是巨大的。这就是深度学习的步骤。神经网络非常擅长为高度结构化的数据提供良好的功能。事实上,我们可以用神经网络识别 Q 函数,它将状态和动作作为输入并输出相应的 Q 值:

Q(state; action) = value

深度神经网络最常见的实现如下图所示:

图 4:深度 Q 神经网络的通用实现

或者, 可以将状态作为输入,并为每个可能的动作生成相应的值:

Q(state) = 每个可能的操作值

这个优化的实现可以在下图中看到:

图 5:深度 Q 神经网络的优化实现

最后的方法在计算上是有利的,因为要更新 Q 值(或选择最高的 Q 值),我们只需要通过网络向前迈出一步,我们将立即获得所有可用操作的所有 Q 值。

Cart-Pole 问题

我们将建立一个深度神经网络,可以学习通过 RL 玩游戏。更具体地说,我们将使用深度 Q 学习训练智能体玩 Cart-Pole 游戏。

在这个游戏中, 自由摆动杆连接到推车。推车可以向左和向右移动,目标是尽可能长时间保持杆直立:

图 6:Cart-Pole

我们使用 OpenAI Gym 模拟这个游戏。我们需要导入所需的库:

import gym
import tensorflow as tf
import numpy as np
import time

让我们创建 Cart-Pole 游戏环境:

env = gym.make('CartPole-v0')

初始化环境,奖励列表和开始时间:

env.reset()
rewards = []
tic = time.time()

这里使用env.render()语句来显示运行模拟的窗口:

for _ in range(1000):
    env.render()

env.action_space.sample()被传递给env.step()语句以构建模拟的下一步:

state, reward, done, info = \
           env.step\
           (env.action_space.sample())

在 Cart-Pole 游戏中,有两种可能的动作:向左或向右移动。因此,我们可以采取两种操作,编码为 0 和 1。

在这里,我们采取随机行动:

rewards.append(reward)
    if done:
        rewards = []
        env.reset()
toc = time.time()

10 秒后,模拟结束:

if toc-tic > 10:
    env.close()

要关闭显示模拟的窗口,请使用env.close()

当我们运行模拟时,我们有一个奖励列表,如下所示:

[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]

在极点超过一定角度后,游戏重置。对于正在运行的每个帧,模拟返回 1.0 的奖励。游戏运行的时间越长,获得的奖励就越多。因此,我们的网络的目标是通过保持杆垂直来最大化奖励。它将通过向左和向右移动推车来完成此操作。

针对 Cart-Pole 问题的深度 Q 网络

我们使用 Bellman 方程再次训练我们的 Q 学习智能体:

这里,s是状态,a是动作,s'是状态s和动作a的下一个状态。

之前,我们使用这个等式来学习 Q 表的值。但是,这个游戏有很多状态可供使用。状态有四个值:推车的位置和速度,以及杆的位置和速度。这些都是实数值,所以如果我们忽略浮点精度,我们实际上有无限状态。然后,我们将用一个近似于 Q 表查找功能的神经网络替换它,而不是使用表格。

通过将状态传递到网络来计算 Q 值,而输出将是每个可用动作的 Q 值,具有完全连接的隐藏层:

图 7:深度 Q-Learning

在这个 Cart-Pole 游戏中,我们有四个输入,一个用于状态中的每个值;和两个输出,每个动作一个。网络权重更新将通过选择动作并使用所选动作模拟游戏来进行。这将把我们带到下一个状态然后再到奖励。

以下是用于解决 Cart-Pole 问题的神经网络的简短代码片段:

import tensorflow as tf
class DQNetwork:
    def __init__(self,\
                 learning_rate=0.01, \
                 state_size=4,\
                 action_size=2, \
                 hidden_size=10,\
                 name='DQNetwork'):

隐藏层由两个完全连接的层组成,具有 ReLU 激活:

self.fc1 =tf.contrib.layers.fully_connected\
                       (self.inputs_,\
                        hidden_size)
            self.fc2 = tf.contrib.layers.fully_connected\
                       (self.fc1,\
                        hidden_size)

输出层是线性输出层:

self.output = tf.contrib.layers.fully_connected\
                          (self.fc2,\
                           action_size,activation_fn=None)

经验重放方法

近似函数可能会受到非独立且相同分布和非平稳数据(状态之间的相关性)的影响。

使用经验重放方法可以克服这种问题。

在智能体和环境之间的交互过程中,所有经验(stateactionrewardnext_state)都保存在重放内存中,这是固定大小的内存并以先进先出(FIFO)的方式运行。

这是重放内存类的实现:

from collections import deque
import numpy as np
class replayMemory():
    def __init__(self, max_size = 1000):
        self.buffer = \
                    deque(maxlen=max_size)
    def build(self, experience):
        self.buffer.append(experience)
    def sample(self, batch_size):
        idx = np.random.choice\
              (np.arange(len(self.buffer)), 
                               size=batch_size, 
                               replace=False)
        return [self.buffer[ii] for ii in idx]

这将允许在网络训练期间使用在重放存储器中随机获取的小批量经验,而不是一个接一个地使用最近的经验。

使用经验重放方法有助于缓解顺序训练数据的问题,这可能导致算法保持在局部最小值,从而使其无法获得最佳解决方案。

利用和探索

每当智能体必须选择要采取的行动时,它基本上有两种方式可以执行其策略。第一种模式称为利用,包括根据目前获得的信息,即过去和存储的经验,做出最佳决策。此信息始终作为值函数提供,该函数表示哪个操作为每个状态 - 操作对提供最大的最终累积回报。

第二种模式称为探索,它是一种决策策略,不同于目前认为最优的策略。

探索阶段非常重要,因为它用于收集未探测状态的信息。实际上,只有执行最佳操作的智能体才有可能始终遵循相同的操作顺序,而没有机会探索并发现可能存在从长远来看可能导致的策略更好的结果,即使这意味着立即获得的收益更低。

最常用于在利用和探索之间达成正确妥协的策略是贪婪的策略。它代表了一种选择动作的方法,基于使用均匀概率分布来选择随机动作的可能性。

深度 Q-Learning 训练算法

让我们看看如何构建一个深度 Q-Learning 算法来解决 Cart-Pole 问题。

该项目相当复杂。为此原因。它已被细分为几个文件模块:

  • DQNetwork.py:实现深度神经网络
  • memory.py:实现经验重放方法
  • start_simulation.py:创建我们想要解决的 Cart-Pole 环境
  • solve_cart_pole.py:用经过训练的神经网络解决 Cart-Pole 环境
  • plot_result_DQN.py:绘制最后的奖励与剧集
  • deepQlearning.py:主程序

以下命令提供了deepQlearning.py文件实现的简要说明:

import tensorflow as tf
import gym
import numpy as np
import time
import os
from create_cart_pole_env import *
from DQNetwork import *
from memory import *
from solve_cart_pole import *
from plot_result_DQN import *

接下来要做的是定义用于此实现的超参数,因此我们需要定义要学习的最大剧集数,一集中的最大步数以及未来的奖励折扣:

train_episodes = 1000
max_steps = 200
gamma = 0.99

探索参数是探索开始时的探索概率,最小探索概率和探索概率的指数衰减率:

explore_start = 1.0
explore_stop = 0.01
decay_rate = 0.0001

网络参数是每个隐藏 Q 网络层中的单元数和 Q 网络学习率:

hidden_size = 64
learning_rate = 0.0001

定义以下内存参数:

memory_size = 10000             
batch_size = 20

然后我们有大量的经验来预先记录内存:

pretrain_length = batch_size

现在我们可以创建环境并启动 Cart-Pole 模拟:

env = gym.make('CartPole-v0')
start_simulation(env)

接下来,我们使用hidden_sizelearning_rate超参数实例化 DNN:

tf.reset_default_graph()
deepQN = DQNetwork(name='main', hidden_size=64, \
                  learning_rate=0.0001)

最后,我们重新初始化模拟:

env.reset()

让我们采取随机步骤,从中我们可以获得状态和奖励:

state, rew, done, _ = env.step(env.action_space.sample())

实例化replayMemory对象以实现经验重放方法:

memory = replayMemory(max_size=10000)

使用memory.build方法获取随机动作的块,来存储相对经验,状态和动作:

pretrain_length= 20
for j in range(pretrain_length):
    action = env.action_space.sample()
    next_state, rew, done, _ = \
                env.step(env.action_space.sample())
    if done:
        env.reset()
        memory.build((state,\
                      action,\
                      rew,\
                      np.zeros(state.shape)))
        state, rew, done, _ = \
               env.step(env.action_space.sample())
    else:
        memory.build((state, action, rew, next_state))
        state = next_state

通过获得的新经验,我们可以进行神经网络的训练:

rew_list = []
train_episodes = 100
max_steps=200
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    step = 0
    for ep in range(1, train_episodes):
        tot_rew = 0
        t = 0
        while t < max_steps:
            step += 1
            explore_p = stop_exp + (start_exp - stop_exp)*\
                        np.exp(-decay_rate*step)
            if explore_p > np.random.rand():
                action = env.action_space.sample()
            else:

然后我们计算 Q 状态:

Qs = sess.run(deepQN.output, \
                              feed_dict={deepQN.inputs_: \
                                         state.reshape\
                                         ((1, *state.shape))})

我们现在可以获得动作:

action = np.argmax(Qs)
            next_state, rew, done, _ = env.step(action)
            tot_rew += rew
            if done:
                next_state = np.zeros(state.shape)
                t = max_steps
                print('Episode: {}'.format(ep),
                      'Total rew: {}'.format(tot_rew),
                      'Training loss: {:.4f}'.format(loss),
                      'Explore P: {:.4f}'.format(explore_p))
                rew_list.append((ep, tot_rew))
                memory.build((state, action, rew, next_state))
                env.reset()
                state, rew, done, _ = env.step\
                                         (env.action_space.sample())
            else:
                memory.build((state, action, rew, next_state))
                state = next_state
                t += 1
            batch_size = pretrain_length               
            states = np.array([item[0] for item \
                               in memory.sample(batch_size)])
            actions = np.array([item[1] for item \
                                in memory.sample(batch_size)])
            rews = np.array([item[2] for item in \
                             memory.sample(batch_size)])
            next_states = np.array([item[3] for item\
                                    in memory.sample(batch_size)])

最后,我们开始训练智能体。训练很慢,因为它渲染的帧比网络可以训练的慢:

target_Qs = sess.run(deepQN.output, \
                                 feed_dict=\
                                 {deepQN.inputs_: next_states})
            target_Qs[(next_states == \
                       np.zeros(states[0].shape))\
                      .all(axis=1)] = (0, 0)
            targets = rews + 0.99 * np.max(target_Qs, axis=1)
            loss, _ = sess.run([deepQN.loss, deepQN.opt],
                                feed_dict={deepQN.inputs_: states,
                                           deepQN.targetQs_: targets,
                                           deepQN.actions_: actions})
 env = gym.make('CartPole-v0')

要测试模型,我们调用以下函数:

solve_cart_pole(env,deepQN,state,sess)
 plot_result(rew_list)

这里是用于测试购物车杆问题的神经网络实现:

import numpy as np
def solve_cart_pole(env,dQN,state,sess):
    test_episodes = 10
    test_max_steps = 400
    env.reset()
    for ep in range(1, test_episodes):
        t = 0
        while t < test_max_steps:
            env.render() 
            Qs = sess.run(dQN.output, \
                          feed_dict={dQN.inputs_: state.reshape\
                                     ((1, *state.shape))})
            action = np.argmax(Qs)
            next_state, reward, done, _ = env.step(action)
            if done:
                t = test_max_steps
                env.reset()
                state, reward, done, _ =    
                               env.step(env.action_space.sample())
            else:
                state = next_state
                t += 1

最后,如果我们运行deepQlearning.py脚本,我们应该得到这样的结果:

[2017-12-03 10:20:43,915] Making new env: CartPole-v0
[]
Episode: 1 Total reward: 7.0 Training loss: 1.1949 Explore P: 0.9993
Episode: 2 Total reward: 21.0 Training loss: 1.1786 Explore P: 0.9972
Episode: 3 Total reward: 38.0 Training loss: 1.1868 Explore P: 0.9935
Episode: 4 Total reward: 8.0 Training loss: 1.3752 Explore P: 0.9927
Episode: 5 Total reward: 9.0 Training loss: 1.6286 Explore P: 0.9918
Episode: 6 Total reward: 32.0 Training loss: 1.4313 Explore P: 0.9887
Episode: 7 Total reward: 19.0 Training loss: 1.2806 Explore P: 0.9868
……
Episode: 581 Total reward: 47.0 Training loss: 0.9959 Explore P: 0.1844
Episode: 582 Total reward: 133.0 Training loss: 21.3187 Explore P: 0.1821
Episode: 583 Total reward: 54.0 Training loss: 42.5041 Explore P: 0.1812
Episode: 584 Total reward: 95.0 Training loss: 1.5211 Explore P: 0.1795
Episode: 585 Total reward: 52.0 Training loss: 1.3615 Explore P: 0.1787
Episode: 586 Total reward: 78.0 Training loss: 1.1606 Explore P: 0.1774
…….
Episode: 984 Total reward: 199.0 Training loss: 0.2630 Explore P: 0.0103
Episode: 985 Total reward: 199.0 Training loss: 0.3037 Explore P: 0.0103
Episode: 986 Total reward: 199.0 Training loss: 256.8498 Explore P: 0.0103
Episode: 987 Total reward: 199.0 Training loss: 0.2177 Explore P: 0.0103
Episode: 988 Total reward: 199.0 Training loss: 0.3051 Explore P: 0.0103
Episode: 989 Total reward: 199.0 Training loss: 218.1568 Explore P: 0.0103
Episode: 990 Total reward: 199.0 Training loss: 0.1679 Explore P: 0.0103
Episode: 991 Total reward: 199.0 Training loss: 0.2048 Explore P: 0.0103
Episode: 992 Total reward: 199.0 Training loss: 0.4215 Explore P: 0.0102
Episode: 993 Total reward: 199.0 Training loss: 0.2133 Explore P: 0.0102
Episode: 994 Total reward: 199.0 Training loss: 0.1836 Explore P: 0.0102
Episode: 995 Total reward: 199.0 Training loss: 0.1656 Explore P: 0.0102
Episode: 996 Total reward: 199.0 Training loss: 0.2620 Explore P: 0.0102
Episode: 997 Total reward: 199.0 Training loss: 0.2358 Explore P: 0.0102
Episode: 998 Total reward: 199.0 Training loss: 0.4601 Explore P: 0.0102
Episode: 999 Total reward: 199.0 Training loss: 0.2845 Explore P: 0.0102
[2017-12-03 10:23:43,770] Making new env: CartPole-v0
>>>

随着训练损失减少,总奖励增加。

在测试期间,推车杆完美平衡:

图 8:解决了 Cart-Pole 问题

为了可视化训练,我们使用了plot_result()函数(它在plot_result_DQN.py函数中定义)。

plot_result()函数绘制每集的总奖励:

def plot_result(rew_list):
    eps, rews = np.array(rew_list).T
    smoothed_rews = running_mean(rews, 10)
    smoothed_rews = running_mean(rews, 10)  
    plt.plot(eps[-len(smoothed_rews):], smoothed_rews)
    plt.plot(eps, rews, color='grey', alpha=0.3)
    plt.xlabel('Episode')
    plt.ylabel('Total Reward')
    plt.show()

以下绘图显示,每个事件的总奖励随着智能体改进其对值函数的估计而增加:

总结

许多研究人员认为,RL 是我们创造人工智能的最好方法。这是一个令人兴奋的领域,有许多未解决的挑战和巨大的潜力。虽然一开始看起来很有挑战性,但开始使用 RL 并不是那么困难。在本章中,我们描述了 RL 的一些基本原理。

我们讨论的主要内容是 Q-Learning 算法。它的显着特点是能够在即时奖励和延迟奖励之间做出选择。最简单的 Q-learning 使用表来存储数据。当监视/控制的系统的状态/动作空间的大小增加时,这很快就失去了可行性。

我们可以使用神经网络作为函数逼近器来克服这个问题,该函数逼近器将状态和动作作为输入并输出相应的 Q 值。

按照这个想法,我们使用 TensorFlow 框架和 OpenAI Gym 工具包实现了一个 Q 学习神经网络,以赢得 FrozenLake 游戏。

在本章的最后一部分,我们介绍了深度强化学习。在传统的 RL 中,问题空间非常有限,并且环境中只有少数可能的状态。这是传统方法的主要局限之一。多年来,已经有一些相对成功的方法能够通过近似状态来处理更大的状态空间。

深度学习算法的进步已经在 RL 中引入了新的成功应用浪潮,因为它提供了有效处理高维输入数据(例如图像)的机会。在这种情况下,训练有素的 DNN 可以被视为一种端到端的 RL 方法,其中智能体可以直接从其输入数据中学习状态抽象和策略近似。按照这种方法,我们实现了 DNN 来解决 Cart-Pole 问题。

我们的 TensorFlow 深度学习之旅将在此结束。深度学习是一个非常有成效的研究领域;有许多书籍,课程和在线资源可以帮助读者深入理论和编程。此外,TensorFlow 还提供了丰富的工具来处理深度学习模型。我希望本书的读者成为 TensorFlow 社区的一部分,该社区非常活跃,并希望热心人士尽快加入。

相关文章
|
2天前
|
机器学习/深度学习 PyTorch TensorFlow
Pytorch 与 Tensorflow:深度学习的主要区别(1)
Pytorch 与 Tensorflow:深度学习的主要区别(1)
16 2
|
2天前
|
机器学习/深度学习 人工智能 自然语言处理
使用TensorFlow进行深度学习入门
【5月更文挑战第11天】本文引导读者入门TensorFlow深度学习,介绍TensorFlow——Google的开源机器学习框架,用于处理各种机器学习问题。内容包括TensorFlow安装(使用pip)、核心概念(张量、计算图和会话)以及构建和训练简单线性回归模型的示例。通过这个例子,读者可掌握TensorFlow的基本操作,包括定义模型、损失函数、优化器以及运行会话。
|
2天前
|
机器学习/深度学习 TensorFlow 算法框架/工具
Python深度学习基于Tensorflow(7)视觉处理基础
Python深度学习基于Tensorflow(7)视觉处理基础
22 2
Python深度学习基于Tensorflow(7)视觉处理基础
|
2天前
|
机器学习/深度学习 算法 TensorFlow
Python深度学习基于Tensorflow(6)神经网络基础
Python深度学习基于Tensorflow(6)神经网络基础
17 2
Python深度学习基于Tensorflow(6)神经网络基础
|
2天前
|
机器学习/深度学习 算法 算法框架/工具
Python深度学习基于Tensorflow(5)机器学习基础
Python深度学习基于Tensorflow(5)机器学习基础
16 2
|
2天前
|
机器学习/深度学习 数据可视化 TensorFlow
Python深度学习基于Tensorflow(4)Tensorflow 数据处理和数据可视化
Python深度学习基于Tensorflow(4)Tensorflow 数据处理和数据可视化
18 3
|
2天前
|
机器学习/深度学习 TensorFlow API
Python深度学习基于Tensorflow(3)Tensorflow 构建模型
Python深度学习基于Tensorflow(3)Tensorflow 构建模型
70 2
|
2天前
|
机器学习/深度学习 TensorFlow 算法框架/工具
Python深度学习基于Tensorflow(2)Tensorflow基础
Python深度学习基于Tensorflow(2)Tensorflow基础
17 3
|
2天前
|
机器学习/深度学习 TensorFlow 算法框架/工具
Python深度学习基于Tensorflow(1)Numpy基础
Python深度学习基于Tensorflow(1)Numpy基础
24 5
|
2天前
|
机器学习/深度学习 数据可视化 TensorFlow
【Python 机器学习专栏】使用 TensorFlow 构建深度学习模型
【4月更文挑战第30天】本文介绍了如何使用 TensorFlow 构建深度学习模型。TensorFlow 是谷歌的开源深度学习框架,具备强大计算能力和灵活编程接口。构建模型涉及数据准备、模型定义、选择损失函数和优化器、训练、评估及模型保存部署。文中以全连接神经网络为例,展示了从数据预处理到模型训练和评估的完整流程。此外,还提到了 TensorFlow 的自动微分、模型可视化和分布式训练等高级特性。通过本文,读者可掌握 TensorFlow 基本用法,为构建高效深度学习模型打下基础。