自己做的网站有什么用,做美股的数据网站,wordpress修改登录地址,专业团队黑人文章目录 一个柯基的例子为什么RNN or CNN Hi#xff0c;你好。我是茶桁。 这节课开始#xff0c;我们将会讲一个比较重要的一种神经网络#xff0c;它对应了咱们整个生活中很多类型的一种问题结构#xff0c;它就是咱们的RNN网络。
咱们首先回忆一下#xff0c;上节课咱… 文章目录 一个柯基的例子为什么RNN or CNN Hi你好。我是茶桁。 这节课开始我们将会讲一个比较重要的一种神经网络它对应了咱们整个生活中很多类型的一种问题结构它就是咱们的RNN网络。
咱们首先回忆一下上节课咱们学到了一些深度学习的一些进阶基础。 学了很多神经网络的Principles, 就是它的一些很重要的概念比方层数维度。再然后咱们讲了Optimizer 一些优化方式。还有weights的initialization初始化等等。
那么大家具备了这些知识之后那我们基本上已经能够解决常见的大概90%的机器学习问题了。
我们现实生活中绝大多数的机器学习问题或者说识别问题都可以把它抽象成要么是分类要么是回归问题。
一个柯基的例子
我们来一个例子比方说一张图片里这个是什么动物这显然是一个分类问题。 但是我们对这个图片的多个物体是什么还有位置标注出来那这个在里面前面会有一段是一个分类问题后面还有一个长的向量又会是一个回归问题。
我们只要知道分类和回归最大的区别就是一个返回的是一个类别另外一个返回的是一个真正的数值。
那么接下来我们要正是的讲一下两种神经网络RNN和CNN。这两个的目的是用来加速解决我们之前遇到的分类问题或者回归问题。
在这些LSTM和CNN之类的高级的方法出现之前其实我们用最直接的神经网络是可以解决所有的问题。
我们还是来看上面的那个例子还是那张图片如果要去分类看这图片里的是什么动物我们把它形式化的表述一下。
假设我们这张图片现在是258*258的那每一张图片进来之后这个图片的饿背后其实都是一个向量
# 258 * 258
from PIL import Image
import numpy as npexample_img Image.open(assets/Corgi.png)
example_img np.array(example_img)print(example_img.shape)---
(429, 696, 3)我们可以看到这张图片在计算机里保存的时候是(429, 696, 3)这样的一组数字。
plt.imshow(example_img)我们用plt展示出来就是这样。
我们现在就可以讲整个图片变成一个向量然后把它从立方体变的拉平
example_img example_img.reshape(1, -1)
print(example_img)---
[[120 150 88 ... 43 39 38]]那现在我们要给这个图片做分类
class Model(nn.Module):def __init__(self, input_dimension, categorical):super(Model, self).__init__()self.linear nn.Linear(in_featuresinput_dimension, out_featurescategorical)self.softmax nn.Softmax()def forward(self, x):predict self.softmax(self.linear(x))return predict...这里我们暂停一下来说说这段代码中的super(...)为了避免有些小伙伴Python基础不太好这里说明一下。
如果有从我Python基础课就看过来的小伙伴应该知道我在面向对象的时候应该是讲过这个方法。这个是为了在继承父类的时候我们在重写父类方法的时候依然可以调用父类方法。方式就是super().父类方法名()。有需要补Python基础的可以回头将我写的Python基础课程好好再看一遍。
好我们继续回过头来讲我们定义好这个Model之后将图片数据变成一个PyTorch能够处理的一个example当作训练数据传入train_x。
train_x torch.from_numpy(example_img)
print(shape:{}, \ntrain_x:{}.format(train_x.shape, train_x))---
shape:torch.Size([1, 895752]),
train_x:tensor([[120, 150, 88, ..., 43, 39, 38]], dtypetorch.uint8)然后进入线性函数传入in_features为train_x.shape[1] 把它变成一个10分类再把test_model运行一下将我们的train_x输入进去就可以了。
test_model Model(input_dimensiontrain_x.shape[1], categorical10)
output test_model(train_x.float())这样的话, 我们就可以产生出一个Softmax有了这个Softmax在这我们如果有很多个x它就会对应我们很多个已知的y。
然后我们在这里定义一个loss
criterion torch.nn.CrossEntropyLoss()再之后我们在做线性的时候之前肯定是有一些ytrue数据的肯定是知道它的y的写个循环它就不断的可以去训练。
接着我们可以得到这个它的权重那么在这里这是一张图片如果这个图片要做回归要给这个图片打分那么将out_features换成1就可以了。
我们在Model里不断的去改它的东西让它的输出能够满足就可以了。
不管是用户数据还是气象数据、天文数据、图片、文字我们都可以把它变成这样的一个x向量。变成x向量之后只要送到一个模型里面这个模型它能够去做优化做些调整。那么它就能够去不断的去做优化。
当然我们这里还缺一个optimizer
optimizer torch.optim.SGD(test_model.parameters(), lr1e-3)我们定义了一个SGD优化器learning_rate设置了一下给了一个初始的学习率。
然后呢再不断的去循环它就可以了
# 定义虚拟的y
lable np.random.randint(0,2,10)
train_y torch.from_numpy(np.array([lable])).float()for t in range(100):y_true train_yy_predict test_model(train_x.float())print(y_true.shape)print(y_predict.shape)print(loss)我们现在可以将criterion假如到循环里来计算一下loss了。
for t in range(100):...loss criterion(y_predict, y_true)就是说我们之前学习的这些内容不管是图片还是用户的数据、或者文字其实都是可以变成一个向量再把向量送入到定义好的模型里求出它的结果。
再经过反复的运作反复的调试来更新它的数据。
为什么RNN or CNN
那为什么我们还要学习RNN和CNN这些东西呢我们刚开始学的wxb的形式可以把任意的x变成其它的一个output 但是它在解决一些问题的时候效果就不是太好。
比方说啊我们要识别一个图像到底是什么的时候wxb它是给每一个x一个权重, w x i b w x_i b wxib, 然后最后产出一个值。
但是图像我们是希望给中间一个区域一个平分可是现在是一个点一个点的。
例如我们输入是一个x输出是一个y。x它包含了多个x:{x1, x2, x3, …, xn}那y的输出呢它是和多个x有关系。如果是在一个曲线上我们取几个点, {output1, output2, output3}, 那么这个output3就不止和 x ⃗ 3 \vec x_3 x 3有关系它和前面的output2, output1都有关系。
也就是说当下这一时刻的数据其实不仅取不仅取决于今天发生的一些事情还取决于昨天前天甚至大前天发生的事情。
但是我们如果直接进行wxib的话这里xix3wx3b我们期望输出一个output3这样就忽略了前边的这些事情。
与此类似的还有我们写文章当前这个字和前面是什么字应该是有依赖关系的。其实把它抽象一下的话会发现在现实生活中其实有很多种依赖关系。
我们之前讲的wxb其实是一对一。 虽然x的维度可能会很大y输出的维度也可能很大但是它一个x就只对应输出一个y。
而除了one to one 之外我们还有一些其他的类别 one to many就是x输入之后最后会输出多个y。比方说咱们输入的是一个类别输出的是一篇文章分别是第一个单词第二个单词和第三个单词。
我们会发现这三个输出的单词前后是有相关性的。这种就属于是一对多输出的的这些内容是独立的个体但是它们之间有相关性。
后面的many to one典型的一个应用你给他输入一句话输出这个地方这句话到底是表示正向的还是负向的。那么这句话其实每个单词之间是有依赖关系的而输出的是一个值。
那many to many里前边输入的这个input是一个序列有依赖关系。输出也是一个序列有依赖关系。那么这会是一个什么比方我们的机器翻译就有可能是这样一个关系对吧还有比方说我们会去做那个文本的阅读理解文本的摘要。
那还有一个many to many和第一个有什么区别呢它其实只是更加的实时比如说同声传译。
对于这些所有的问题我们给它抽象一下它每一步的输出就像我们之前学过递归函数一样是和前一步的输出有关系还和当前这一步的输入有关系, 我们其实学过最典型的一个依赖关系就是这样就是斐波那契数列或者求阶乘
def fib(n):if n 0 or n 1: return 1else:return fib(n-1) fib(n-2)def fac(n):if n 0: return 1else: return n*fac(n-1)for i in range(10): print({}\t{}.format(fib(i), fac(i)))那么这个怎么实现的我们要实现这个有多种方法我们可以来看一个具体的案例
class RNN(nn.Module):# implement RNN from scratch rather than ysubf nn.RNNdef __init__(self, input_size, hidden_size, output_size):super(RNN, self).__init__()self.hidden_size hidden_sizeself.i2h nn.Linear(input_size hidden_size, hidden_size)self.i2o nn.Linear(input_size hidden_size, output_size)self.softmax nn.LogSoftmax(dim1)def forward(self, input_tensor, hidden_tensor):combined torch.cat((input_tensor, hidden_tensor), 1)hidden self.i2h(combined)output self.i2o(combined)这是一个非常经典的RNN的模型我们来一起来分析它的构成。
在构造函数内输入了一个input_sizex向量还有一个hidden_size。然后在下面做了一个i2h的线性变化这个线性变化它接受一个的两个参数 in_features是input_size hidden_size, out_features是hidden_size。
现在有一个 x ⃗ \vec x x 和一个 h ⃗ \vec h h 将两个向量相加输入进入然后会输出一个 v e c h vec h vech一样大小的东西。
然后下面还有一个i2o 它是将input_size hidden_size输入之后输出一个output_size一样大小的东西。
在输出这两个之后我们将output_size大小的这个向量输入到Softmax里面就会变成一个概率分布。
然后它继续forward的时候继续向前运算的时候它的输入是input和hidden那它在这里如果我们要求训练
def train(line_tensor, category_tensor):hidden rnn.init_hidden()for i in range(line_tensor.size()[0]):output, hidden rnn(line_tensor[i], hidden)这里它有很多的tensor比如我们的x:[x1, x2, ..., xn], 这个tensor就是这些个x。那么它在做训练的第一步会取最前面的这个x向量这个x向量刚开始会有一个随机的hidden向量这个时候关键的地方就来了就是它不断的重复:output, hidden rnn(line_tensor[i], hidden), 我们来看这个hidden就会一次一次的送进去做更新。
hidden一开始是随机的之后t时刻的hidden的值是由上一时刻也就是t-1时刻的x和hidden来影响的。
h0 - random
(x0, h0) - output1, h1
(x1, h1) - output2, h2
...这样输出的output2不仅是x1的影响也是受到x0的影响的这样前后的关系就被连接起来了。
就比如说我们输入的是一段文字就比说ChaHeng输入C的时候我们会得到一个hidden 然后计算h时候我们又会得到一个hidden, 一直到最后一个g那我们算这一步的时候它既包含了g这个字母 还包含了之前n的hidden向量。那n再往上一直到C都相关这样它就实现了传递的效果。
那这个做法有两个人分别提出来了两种。 之前我们将神经网络建模为: y t σ ( W x t b ) y t 1 σ ( W x t 1 b ) \begin{align*} y_t \sigma(Wx_t b) \\ y_{t1} \sigma(Wx_{t1} b) \end{align*} ytσ(Wxtb)yt1σ(Wxt1b)
现在我们将其更新为两两种方法一个是Elman network: h t σ h ( W h x t U h h t − 1 b h ) y t σ y ( W y h t b y ) \begin{align*} h_t \sigma_h(W_hx_t U_hh_{t-1}b_h) \\ y_t \sigma_y(W_yh_t b_y) \end{align*} htytσh(WhxtUhht−1bh)σy(Wyhtby)
还有一个是Jordan networks: h t σ h ( W h x t U h y t − 1 b h ) y t σ y ( W y h t b y ) \begin{align*} h_t \sigma_h(W_hx_t U_hy_{t-1}b_h) \\ y_t \sigma_y(W_yh_t b_y) \end{align*} htytσh(WhxtUhyt−1bh)σy(Wyhtby)
我们看一下区别其实就是为了加上非线性变化。给h加了一个非线性变化再给y加了一个非线性变化。
这两个人都是很著名的计算机科学家他们提出来的模型有区别一个是一直在传递这个h一个是一直在传递y。但是都实现了yt时刻和xt有关也和x_{t-1}有关。这两个都实现了这样的一种功能只不过它们中间一直传递的东西不太一样。
这个就是RNN的内核它的内核就是这个东西。
我们接着就来看一个案例这个案例中的数据是一个盈利数据, 还是老样子数据集我就放在文末了。
我们这里是一个两个月每天的盈利指数其中2点几的是盈利比较多1点几的就是盈利比较少的。
timeserise_revenue pd.read_csv(~/mount/Sync/data/AI_Cheats/time_serise_revenue.csv)
sales_data pd.read_csv(~/mount/Sync/data/AI_Cheats/time_serise_sale.csv)timeserise_revenue.drop(axis1, columnsUnnamed: 0, inplaceTrue)
sales_data.drop(axis1, columnsUnnamed: 0, inplaceTrue)数据上我就不展示了大家自己拿到后查看一下。我们现在要做的是是想根据它前十天的一个数据来预测一下第11天的数据。
很简单的方法咱们可以写一个全连接的网络
class FullyConnected(nn.Module):def __init__(self, x_size, hidden_size, output_size):super(FullyConnected, self).__init__()self.hidden_size hidden_sizeself.linear_with_tanh nn.Sequential(nn.Linear(10, self.hidden_size),nn.Tanh(),nn.Linear(self.hidden_size, self.hidden_size),nn.Tanh(),nn.Linear(self.hidden_size, output_size))def forward(self, x):yhat self.linear_with_tanh(x)return yhat我们输入10个值对它进行线性变化再给它进行一个非线性变化然后重复一遍最后再来一次线性变化这样就是最简单的一种线性和非线性变化的网络。
然后我们处理一下数据设置一下相关参数
sales_data.drop(axis1, columnsUnnamed: 0, inplaceTrue)
source_data sales_datan_epochs 30
hidden_size 2 # try to change this parameters
n_layers 1
batch_size 5
seq_length 10
n_sample_size 50x_size 1fc_model FullyConnected(x_size, hidden_size, output_sizeseq_length)
fc_model fc_model.double()criterion nn.MSELoss()
optimizer optim.SGD(fc_model.parameters(), lr0.01)fc_losses np.zeros(n_epochs) plt.imshow(fc_model.state_dict()[linear_with_tanh.0.weight])显示了一下一开始的权重。
之后我们来看一下整个的训练过程
data_loader torch.utils.data.DataLoader(source_data.values, batch_sizeseq_length, shuffleTrue)for epoch in range(n_epochs):epoch_losses []for iter_, t in enumerate(data_loader):random_index random.randint(0, t.shape[-1] - seq_length - 1)train_x t[:, random_index: random_indexseq_length]train_y t[:, random_index 1: random_index seq_length 1]outputs fc_model(train_x.double())optimizer.zero_grad()loss criterion(outputs, train_y)loss.backward()optimizer.step()epoch_losses.append(loss.detach())fc_losses[epoch] np.mean(epoch_losses)传入的data_loader是每一次随机的取期望的10个数字,这个数字我们就会根据序列来取出x和y, 然后把x送到模型里边得到outputs,得到outputs之后又出现熟悉的面孔, 我们求它的loss再通过它的loss做反向传播。
optimizer做step就是做全程的更新。
之后我们可以将每次循环的结果打印出来看看
for epoch in range(n_epochs):...for iter_, t in enumerate(data_loader):...if iter_ 0:plt.clf()plt.ion()plt.title(Epoch {}, iter {}.format(epoch, iter_))plt.plot(torch.flatten(outputs.detach()),r-,linewidth1,labelOutput)plt.plot(torch.flatten(train_y),c-,linewidth1,labelLabel)plt.plot(torch.flatten(train_x),g-,linewidth1,labelInput)plt.draw()plt.pause(0.05)我们就不全展示了大家可以自行去运行一下。 红色是预测值绿色是输入值蓝色是实际值。这里我只放了第一张和第30张也就是本次循环的最后一张。
那一开始预测出来值没有和我们实际的值相符到了30的相较而言是比较相符了。
我们看看它的loss是否如预期的下降了
plt.plot(fc_losses)看完全连接的模型再来看看RNN的模型做一个非常简单的RNN模型那首先还是定义模型
class SimpleRNN(nn.Module):def __init__(self, x_size, hidden_size, n_layers, batch_size, output_size):super(SimpleRNN, self).__init__()self.hidden_size hidden_sizeself.n_layers n_layersself.batch_size batch_sizeself.rnn nn.RNN(x_size, hidden_size, n_layers, batch_firstTrue)self.out nn.Linear(hidden_size, output_size) # 10 in and 10 outdef forward(self, inputs, hiddenNone):hidden self.__init__hidden()output, hidden self.rnn(inputs.float(), hidden.float())output self.out(output.float());return output, hiddendef __init__hidden(self):hidden torch.zeros(self.n_layers, self.batch_size, self.hidden_size, dtypetorch.float64)return hidden我们输入的是x_size然后然后定义一个hidden_size。这里注意啊hidden_size是可以改的越大可以表示的中间层的信息就越多但意味着需要更多的数据去训练它。
然后在forward里可以看到每一步会输出一个output到最后一步的时候我们把output做一个线性变化就可以变成期望的这个结果。
那这个RNN模型其实非常的简单就是进了一个RNN然后做了一个线性变化把output做成线性变化。
然后我们来看看具体表现如何 那首先一样的是定义参数数据可以用上一次整理过的数据不需要再做一次了
n_epochs 30
hidden_size 2 # try to change this parameters
n_layers 1
batch_size 5
seq_length 10
n_sample_size 50x_size 1
output_size 1hidden Nonernn_model SimpleRNN(x_size, hidden_size, n_layers, seq_length, output_size)criterion nn.MSELoss()
optimizer optim.SGD(rnn_model.parameters(), lr0.01)rnn_losses np.zeros(n_epochs) 然后我们就可以来跑一下了。
data_loader torch.utils.data.DataLoader(source_data.values, batch_sizeseq_length, shuffleTrue)for epoch in range(n_epochs):for iter_, t in enumerate(data_loader):if t.shape[0] ! seq_length: continue random_index random.randint(0, t.shape[-1] - seq_length - 1)train_x t[:, random_index: random_indexseq_length]train_y t[:, random_index 1: random_index seq_length 1]outputs, hidden rnn_model(train_x.double().unsqueeze(2), hidden)optimizer.zero_grad()loss criterion(outputs.double(), train_y.double().unsqueeze(2))loss.backward()optimizer.step()epoch_losses.append(loss.detach())rnn_losses[epoch] np.mean(epoch_losses)那RNN模型其实从第三轮的时候效果就已经出现了我们的x一样改变了一个模型之后拟合的效果就不一样了。
我们来看看它的loss RNN模型跑下来loss是下降到了0.67左右那我们之前的全连接模型的loss是在0.8以上还是有一些区别的。我们可以将两个模型的loss打印到一张图上就更能看出来两个模型的区别了。
plt.plot(rnn_losses, cred)
plt.plot(fc_losses, cgreen)就可以看到非常明显。
举这个例子作用是想说明wxb加上非线性变化这种形式其实也能解决问题但是遇到时间相关序列相关的问题的时候解决效果就没有RN模型这么好。
为什么没有RNN模型好呢因为RNN模型在这个过程中每一步把前一步的hidden的影响给它保留了下来。就是说它每一步的输出的时候不是单纯的考虑这一步的输出把之前每一步的x的值其实都保留下来了。这个区别就是为什么要有RNN以及大家之后什么时候用RNN。
因为我这边只是做个测试所以仅仅做了30次epoch那之后大家可以尝试一下将epoch改成200或者更多来看看具体loss会下降到什么程度。
好文章最后就是本文所用的数据集了 time_serise_revenue.csv 链接: https://pan.baidu.com/s/1dL9XdBgoi3nC2VOC6w_wnw?pwdqmw6 提取码: qmw6 –来自百度网盘超级会员v6的分享 time_serise_sale.csv 链接: https://pan.baidu.com/s/12wMJHzSZk91YPFcaG-K6Eg?pwd1kmp 提取码: 1kmp –来自百度网盘超级会员v6的分享