Pytorch动态

Tensorflow 就是最典型的静态计算模块.  用 Tensorflow 是先搭建好这样一个计算系统, 一旦搭建好了, 就不能改动了

(也有例外, 比如dynamic_rnn(), 但是总体来说他还是运用了一个静态思维), 所有的计算都会在这种图中流动, 

有时候 RNN 的 time step 不会一样, 或者在 training 和 testing 的时候, batch_size 和 time_step 也不一样, 这时就需要动态计算

动态RNN

######################## 前面代码都一样, 下面开始不同 #########################

################ 那节内容的代码结构 (静态 time step) ##########
for step in range(60):
    start, end = step * np.pi, (step+1)*np.pi   # time steps 都是一样长的
    # use sin predicts cos
    steps = np.linspace(start, end, 10, dtype=np.float32)
    ...


################ 这节内容修改代码 (动态 time step) #########
step = 0
for i in range(60):
    dynamic_steps = np.random.randint(1, 4)  # 随机 time step 长度
    start, end = step * np.pi, (step + dynamic_steps) * np.pi  # different time steps length
    step += dynamic_steps

    # use sin predicts cos
    steps = np.linspace(start, end, 10 * dynamic_steps, dtype=np.float32)

#######################  这下面又一样了 ###########################
    print(len(steps))   # print how many time step feed to RNN

    x_np = np.sin(steps)    # float32 for converting torch FloatTensor
    y_np = np.cos(steps)
    ...

"""
输出的动态time step 长
30
30
10
30
20
30
"""

GPU加速运算

在 GPU 训练可以大幅提升运算速度. 而且 Torch 也有一套很好的 GPU 运算体系.

但是要强调的是: *电脑里有合适的 GPU 显卡(NVIDIA), 且支持 CUDA 模块. 

修改:数据+CNN模块+训练数据+计算图纸

用GPU训练CNN

将数据的形式变成 GPU 能读的形式, 将 CNN 也变成 GPU 能读的形式. 做法就是在后面加上 .cuda()

test_data = torchvision.datasets.MNIST(root='./mnist/', train=False)

# !!!!!!!! 修改 test data 形式 !!!!!!!!! #
test_x = torch.unsqueeze(test_data.test_data, dim=1).type(torch.FloatTensor)[:2000].cuda()/255.   # Tensor on GPU
test_y = test_data.test_labels[:2000].cuda()

 CNN 参数也变成 GPU 兼容形式.

class CNN(nn.Module):
    ...

cnn = CNN()

# !!!!!!!! 转换 cnn 去 CUDA !!!!!!!!! #
cnn.cuda()      # Moves all model parameters and buffers to the GPU.

在 train 的时候, 将每次的training data 变成 GPU 形式. + .cuda()

for epoch ..:
    for step, ...:
        # !!!!!!!! 这里有修改 !!!!!!!!! #
        b_x = x.cuda()    # Tensor on GPU
        b_y = y.cuda()    # Tensor on GPU

        ...

        if step % 50 == 0:
            test_output = cnn(test_x)

            # !!!!!!!! 这里有修改  !!!!!!!!! #
            pred_y = torch.max(test_output, 1)[1].cuda().data.squeeze()  # 将操作放去 GPU

            accuracy = torch.sum(pred_y == test_y) / test_y.size(0)
            ...

test_output = cnn(test_x[:10])

# !!!!!!!! 这里有修改 !!!!!!!!! #
pred_y = torch.max(test_output, 1)[1].cuda().data.squeeze()  # 将操作放去 GPU
...
print(test_y[:10], 'real number')

如果还有要在CPU 上进行的, 比如 plt 的可视化, 需要将这些计算或者数据转移至 CPU.

cpu_data = gpu_data.cpu()

过拟合 (Overfitting)

自负的坏处, 大家也知道, 就是在自己的小圈子里表现非凡, 不过在现实的大圈子里却往往处处碰壁

回归分类的过拟合

QLineSeries 动态 动态fitting_神经网络

QLineSeries 动态 动态fitting_过拟合_02

机器学习模型的自负表现:

这里是一些数据. 如果要你画一条线来描述这些数据, 大多数人都会这么画. 这时蓝线与数据的总误差可能是10.

可是有时候, 机器过于纠结这误差值, 他想把误差减到更小, 来完成他对这一批数据的学习使命. 所以, 他学到的可能几乎经过了每一个数据点, 这样, 误差值会更小 .

但当拿这个模型运用在现实中的时候, 自负就体现出来. 

这时, 之前误差大的蓝线误差基本保持不变 .误差小的 红线误差值突然飙高 , 自负的红线不能成功的表达除了训练数据以外的其他数据.

解决方法

方法一: 增加数据量, 大部分过拟合产生的原因是因为数据量太少了. 如果我们有成千上万的数据, 红线也会慢慢被拉直, 变得没那么扭曲 . 方法二:运用正规化.

QLineSeries 动态 动态fitting_QLineSeries 动态_03

我们简化机器学习的关键公式为 y=Wx . W为机器需要学习到的各种参数. 在过拟合中, W 的值往往变化得特别大或特别小. 

为了不让W变化太大, 我们在计算误差上做些手脚. 原始的 cost 误差计算:cost = 预测值-真实值的平方.

如果 W 变得太大, 就让 cost 也跟着变大, 变成一种惩罚机制.

这里 abs 是绝对值. 这一种形式的 正规化, 叫做 l1 正规化. L2 正规化和 l1 类似, 只是绝对值换成了平方.

其他的l3, l4 也都是换成了立方和4次方等等. 形式类似. 用这些方法就能保证让学出来的线条不会过于扭曲.

QLineSeries 动态 动态fitting_神经网络_04

还有一种专门用在神经网络的正规化的方法, 叫作 dropout.

在训练的时候, 我们随机忽略掉一些神经元和神经联结 , 是这个神经网络变得”不完整”. 用一个不完整的神经网络训练一次.到第二次再随机忽略另一些, 变成另一个不完整的神经网络.

有了这些随机 drop 掉的规则, 我们可以想象其实每次训练的时候, 我们都让每一次预测结果都不会依赖于其中某部分特定的神经元. 像l1, l2正规化一样, 过度依赖的 W , 也就是训练参数的数值会很大, l1, l2会惩罚这些大的 参数. Dropout 的做法是从根本上让神经网络没机会过度依赖.

Dropout 缓解过拟合

做点数据

做10个数据点

QLineSeries 动态 动态fitting_数据_05

import torch
import matplotlib.pyplot as plt

torch.manual_seed(1)    # reproducible

N_SAMPLES = 20
N_HIDDEN = 300

# training data
x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES), 1)
y = x + 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))

# test data
test_x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES), 1)
test_y = test_x + 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))

# show data
plt.scatter(x.data.numpy(), y.data.numpy(), c='magenta', s=50, alpha=0.5, label='train')
plt.scatter(test_x.data.numpy(), test_y.data.numpy(), c='cyan', s=50, alpha=0.5, label='test')
plt.legend(loc='upper left')
plt.ylim((-2.5, 2.5))
plt.show()

搭建神经网络

搭建两个神经网络, 一个没有 dropout, 一个有 dropout.

没有 dropout 的容易出现 过拟合, 那我们就命名为 net_overfitting, 另一个就是 net_dropped

torch.nn.Dropout(0.5) 这里的 0.5 指的是随机有 50% 的神经元会被关闭/丢弃.

net_overfitting = torch.nn.Sequential(
    torch.nn.Linear(1, N_HIDDEN),
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, N_HIDDEN),
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, 1),
)

net_dropped = torch.nn.Sequential(
    torch.nn.Linear(1, N_HIDDEN),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, N_HIDDEN),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, 1),
)

训练

两个神经网络分开训练,训练环境一样

optimizer_ofit = torch.optim.Adam(net_overfitting.parameters(), lr=0.01)
optimizer_drop = torch.optim.Adam(net_dropped.parameters(), lr=0.01)
loss_func = torch.nn.MSELoss()

for t in range(500):
    pred_ofit = net_overfitting(x)
    pred_drop = net_dropped(x)

    loss_ofit = loss_func(pred_ofit, y)
    loss_drop = loss_func(pred_drop, y)

    optimizer_ofit.zero_grad()
    optimizer_drop.zero_grad()
    loss_ofit.backward()
    loss_drop.backward()
    optimizer_ofit.step()
    optimizer_drop.step()

对比测试结果

在这个 for 循环里, 我们加上画测试图的部分.

注意在测试时, 要将网络改成 eval() 形式, 特别是 net_droppednet_overfitting画好图再改回 train() 模式.

QLineSeries 动态 动态fitting_过拟合_06

optimizer_ofit.step()
    optimizer_drop.step()

    # 接着上面来
    if t % 10 == 0:     # 每 10 步画一次图
        # 将神经网络转换成测试形式, 画好图之后改回 训练形式
        net_overfitting.eval()
        net_dropped.eval()  # 因为 drop 网络在 train 的时候和 test 的时候参数不一样.

        ...
        test_pred_ofit = net_overfitting(test_x)
        test_pred_drop = net_dropped(test_x)
        ...

        # 将两个网络改回 训练形式
        net_overfitting.train()
        net_dropped.train()

QLineSeries 动态 动态fitting_数据_07