TensorFlow 是一款用于数值计算的强大的开源软件库,特别适用于大规模机器学习的微调。
import tensorflow as tf
# 载入数据
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
# 建模
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)), # 将输入“压平”,即把多维的输入一维化,只有第一层有输入数据的形状
tf.keras.layers.Dense(128, activation='relu'), # 该层的输出维度或神经元个数和激活函数
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation='softmax')
])
# 编译(优化器、损失和评价)
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
# 训练(训练和验证数据不一样)
model.fit(x_train, y_train, epochs=5)
# 验证
model.evaluate(x_test, y_test, verbose=2)
tf.keras.optimizers.SGD( learning_rate=0.01, momentum=0.0,nesterov=False, name='SGD', **kwargs)
学习率、动量、是否使用nesterov震荡
Adam优化器
tf.keras.optimizers.Adam(learning_rate=0.001)
import tensorflow as tf
import tensorflow.compat.v1 as tf1
string = '12.3'
n1 = tf1.string_to_number(string, out_type=None, name=None) #字符串转为数字
x = 12.3
d1 = tf1.to_double(x, name='ToDouble')#转为64位浮点类型float64
f1 = tf1.to_float(x, name='ToFloat') #转为32位浮点类型float32
i1 = tf1.to_int32(x, name='ToInt32') #转为32位整型int32
i2 = tf1.to_int64(x, name='ToInt64') #转为64位整型–int64
a = [1.8, 2.2]
i3 = tf.cast(a, tf.int32) # 将a或者a.values转换为dtype
t = [ [[1, 1, 1], [2, 2, 2]] , [[3, 3, 3], [4, 4, 4]] ]
tf.shape(t) #返回维度中数据的个数
tf.size(t) #返回数据的元素数量
tf.rank(t) #返回数据维度
t = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(tf.shape(t))
t2 = tf.reshape(t, [3, 3]) #改变tensor的形状
print(tf.shape(t2))
t3 = tf.reshape(t, [3, -1])
print(t3)
t = [2,3] #插⼊维度1进⼊⼀个tensor中
t1 = tf.shape(tf.expand_dims(t, 0)) [1,2]
t2 = tf.shape(tf.expand_dims(t, 1)) [2,1]
t3 = tf.shape(tf.expand_dims(t, -1)) [2,1]
t4 = tf.ones([2,3,5]) #用数字1填充所有维度
t6 = tf.shape(tf.expand_dims(t4, 2)) shape=【2,3,1,5】
t= [[[1, 1, 1], [2, 2, 2]],[[3, 3, 3], [4, 4, 4]],[[5, 5, 5], [6, 6, 6]]]
#对tensor进⾏切⽚操作
t1 = tf.slice(t, [1, 0, 0], [1, 1, 3])
t2 = tf.slice(t, [1, 0, 0], [1, 2, 3])
t3 = tf.slice(t, [1, 0, 0], [2, 1, 3])
t = tf.ones([5,30])
t1, t2, t3 = tf.split(t,3,1) #沿着某⼀维度将tensor分离
print(tf.shape(t1))
print(tf.shape(t2))
print(tf.shape(t3))
t1 = [[1, 2, 3], [4, 5, 6]]
t2 = [[7, 8, 9], [10, 11, 12]]
t3 = tf.concat([t1, t2], 0) #沿着某⼀维度连结tensor
t4 = tf.concat([t1, t2], 1)
x = [1, 4] y = [2, 5] z = [3, 6]
t1 =tf.stack([x, y, z]) #沿着第⼀维stack [[1 4] [2 5] [3 6]]
t2 = tf.stack([x, y, z], axis=1) #axis取0时表示按x轴叠加,取1时表示按y轴叠加#[[1 2 3] [4 5 6]]
t = [[ [[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]],
[[12, 13, 14, 15],[16, 17, 18, 19],[20, 21, 22, 23]] ]] # shape=(1,2,3,4),dims = [3]
t1 = tf.reverse(t, dims) #沿着某维度进⾏序列反转dims = [1]
t2 = tf.reverse(t, dims) #dims = [2]
t3 = tf.reverse(t, dims) [[ [[ 8 9 10 11] [ 4 5 6 7] [ 0 1 2 3]] [[20 21 22 23] [16 17 18 19] [12 13 14 15]] ]]
t = [[1, 2, 3],[4, 5, 6]]
t1 = tf.transpose(t) #调换tensor的维度顺序
t2 = tf.transpose(t, perm=[1, 0]) # [[1 4] [2 5] [3 6]]
indices = [0, 1, 2] depth = 3
t1 = tf.one_hot(indices, depth) #
indices = [0, 2, -1, 1] depth = 3
t2 = tf.one_hot(indices, depth,on_value=5.0, off_value=0.0,axis=-1)
indices = [[0, 2], [1, -1]] depth = 3
t3 = tf.one_hot(indices, depth,on_value=1.0, off_value=0.0,axis=-1) # [ [[1. 0. 0.][0. 0. 1.]]
[[0. 1. 0.][0. 0. 0.]] ]
t = [1, 1, 2, 4, 4, 4, 7, 8, 8]
y, idx = tf.unique(t)
print(y) #t的所有唯一元素
print(idx) #y中每个值的索引
tf.math.ceil([-1.7,-1.5,-0.2,0.2,1.5,1.7,2.0]) #向上取整函数
tf.gather(params,indices,axis=0 ) 从params的axis维根据indices的参数值获取切片
tf.diag(diagonal) 根据主对角线元素生成矩阵
tf.trace(x, name=None)求二维tensor对角值之和
tf.matrix_determinant(input, name=None)求方阵行列式
tf.matrix_inverse(input, adjoint=None,name=None)求逆矩阵
tf.matmul(a,b,transpose_a=False,transpose_b=False,a_is_sparse=False,b_is_sparse=False,name=None)矩阵相乘
tf.complex(real, imag, name=None) 将两实数转换为复数形式
tf.complex_abs(x, name=None) 计算复数的绝对值,即⻓度。
tf.conj(input, name=None) 计算共轭复数
tf.imag(input, name=None)
tf.real(input, name=None)
tf.eye(num_rows,num_columns=None)生成单位阵
tf.fill(dims,value,name=None) fill([2, 3], 9) ==> [[9, 9, 9][9, 9, 9]]
tf.ones(shape,dtype=tf.dtypes.float32,name=None)
⽣成随机张量
tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None)
从“服从指定正态分布的序列”中随机取出指定个数的值,shape: 输出张量的形状,mean: 正态分布的均值,stddev: 正态分布的标准差,dtype: 输出的类型,seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样
tf.truncated_normal(shape, mean, stddev)
产生截断正态分布随机数,取值范围为 [ mean - 2 * stddev, mean + 2 * stddev ]
tf.random_uniform(shape,minval=0, maxval=None, dtype=tf.float32, seed=None)
生成的值在 [minval, maxval) 范围内遵循均匀分布.
tf.random_shuffle() 随机地将张量沿其第一维度打乱
变量:
biases = tf.Variable(tf.zeros([200]),name=“var") 生成一组变量(没有初始化)
const = tf.constant(1.0,name="constant") 创建常量
名称作用域
在TensorFlow 应用程序中,可能会有数以千计的计算节点。如此多节点汇集在一起,难以分析,甚至无法用标准的图表工具来展示。为解决这个问题,一个有效方法就是,为Op/Tensor 划定名称范围。在TensorFlow 中,这个机制叫名称作用域(name scope)
tf.variable_scope() 管理变量命名空间,可以创建同名变量。在tf.variable_scope 中创建的变量,名称. name 中名称前面会加入命名空间的名称,并通过“/” 来分隔命名空间名和变量名。
tf.get_variable(name="foo/bar", shape=[1]) ,可以通过带命名空间名称的变量名来获取其命名空间下的变量。如果变量存在,则使用以前创建的变量,如果不存在,则新创建一个变量。不能创建同名变量。
概念:Pytorch是一个基于python的科学计算包,主要用途为,
测试:
torch.cuda.is_available()
x = torch.rand(5,3) print(x)
print(x)
基本元素: 张量(Tensor)、变量(Variable)、神经网络模块(nn.Module)
神经⽹络模块nn.Module:神经⽹络的接⼝。定义⾃⼰的神经⽹络时,需继承 nn.Module类
torch.nn.Linear(in_features, out_features, bias=True)
⽹络中的全连接层,全连接层输⼊与输出都是⼆维张量,输⼊形状为[batch_size, size]。
in_features:指的是输入的二维张量的大小。
out_features:指的是输出的二维张量的大小,即输出的二维张量的形状为[batch_size,output_size],也代表该全连接层的神经元个数。
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
kernel_size:卷积核的⼤⼩,⽤(H,W)表示HxW的输出,H表示H*H⼤⼩的输出。
stride:卷积步幅,卷积核每次挪动间距
padding:填充操作,控制padding_mode的数⽬。默认为Zero-padding。
dilation:扩张操作,控制kernel点(卷积核点)的间距,默认为1。
group:控制分组卷积,默认不分组。 (对输入feature map进行分组,然后每组分别卷积。)
bias:是否添加偏置,如为真,则在输出中添加⼀个可学习的偏差,默认为True。
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True)
num_features:特征数C
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1)
输⼊: (N,C,H_in,W_in)
输出: (N,C,H_out,W_out)
torch.nn.AvgPool2d(kernel_size, stride=None, padding=0, ceil_mode=False)
ceil_mode:如果为True,则在计算输出形状时使⽤ceil函数替代floor
torch.nn.AdaptiveAvgPool2d(output_size)
output_size: 输出信号的尺⼨
Adaptive Pooling特殊性在于输出张量的⼤⼩是给定的output_size。对于任何输⼊⼤⼩的输⼊,可以将输出尺⼨指定为H*W,但是输⼊和输出特征的数⽬不会变化。
torch.nn.functional.adaptive_avg_pool2d(input, output_size)
torch.nn.ReLU(inplace=False)
inplace:选择是否进⾏原位运算,即x = x+1m = nn.Sigmoid()
input = torch.randn(2)
output = m(input)
input, output
(tensor([-0.8425, 0.7383]), tensor([0.3010, 0.6766]))
4.Tanh:
m = nn.Tanh()
input = torch.randn(2)
output = m(input)
input, output
(tensor([1.3372, 0.6170]), tensor([0.8710, 0.5490]))
函数:torch.nn.RNN(input_size, hidden_size, num_layers)
RNN的输出包含两部分:输出值Y(即output)和 最后⼀个时刻隐含层的输出 h n h_n hn
例:输⼊⼀段中⽂,输出⼀段英⽂。每个中⽂字符⽤100维数据进⾏编码,每个隐含层的维度是20,有4个隐含层。所以input_size = 100,hidden_size = 20,num_layers = 4。设模型已经训练好了,现在有个1个⻓度为10的句⼦做输⼊,那seq_len = 10, batch_size = 1。
import torch
import torch.nn as nn
input_size = 100 # 输⼊数据编码的维度
hidden_size = 20 # 隐含层维度
num_layers = 4 # 隐含层层数
rnn = nn.RNN(input_size=input_size,hidden_size=hidden_size,num_layers=num_layers)
print("rnn:",rnn)
seq_len = 10 # 句⼦⻓度
batch_size = 1
x = torch.randn(seq_len,batch_size,input_size) # 输⼊数据x
h0 = torch.zeros(num_layers,batch_size,hidden_size) # 输⼊数据h0
out, h = rnn(x, h0) # 输出数据
print("out.shape:",out.shape)
print("h.shape:",h.shape)
例如:如果要处理3个句⼦,每个句⼦10个单词,每个单词⽤100维的嵌⼊向量表示
nn.RNN传⼊的Tensor的shape是[10,3,100]
nn.RNNCell传⼊的Tensor的shape是[3,100],将此计算单元运⾏10次
torch.nn.LSTM(input_size, hidden_size, num_layers)
out, (h_t, c_t) = lstm(x, [h_t0, c_t0])
h_t, c_t = lstmcell(x_t, [h_t-1, c_t-1])
nn.LSTM是⼀次性将所有时刻特征输⼊⽹络的。
nn.LSTMCell将序列上的‘每个时刻的数据’分开来处理
CNN使用部分连接层,三个思想为局部性、相同性、不变性
过滤器(Filters、卷积核) 卷积操作 设定参数
1)滤波器的长宽高 HWC
2)步长(Stride)
3)边界填充
注:经过滤器处理后的图像称为特征图,特征图中所有神经元共享相同参数(权重、偏差顷)
基本结构(输入层+卷积层(提取特征)+池化层(压缩特征)+ 全连接层(非线性输出))
卷积层的权重表示:4D张量 [ fh,fw,fn,fn′]
卷积层的偏置项表示:1D张量 [fn]
计算:一个具有5×5过滤器的卷积层,输出尺寸为150×100的200个特征图,带有步幅1和SAME填充。
参数数量:若输入是150×100 RGB图像(三通道),则参数的数量:(5×5×3+1)*200=15,200
200个特征图每一个都包含150×100个神经元,每个都需要计算其5×5×3=75个输入的加权和,总共有75*150*100*200=2.25亿次浮点乘法。
若用32位浮点数表示特征图,卷积层输出将占200×150×100×32=96百万位(约11.4 MB)内存
写代码时,要注意,padding有两个模式,分别是 ‘same’ 和 ‘valid’ ,
padding='same'
表示进行填充,填充的值由算法内部根据卷积核大小计算,目的是让输出尺寸和输入相等;
padding='valid'
表示不进行填充,即是 padding=0,只使用有效的窗口位置,这是默认的选项。
举例子:
问题1. 一个尺寸 55 的特征图,经过 33 的卷积层,步幅(stride)=1,想要输出尺寸和输入尺寸相等,填充(padding)的值应该等于多少?
答:填充的值=(3 - 1)/2 = 1,即每一边填充1层
问题2. 一个尺寸 224224 的特征图,经过 77 的卷积层,步幅(stride)=2,想要输出尺寸和输入尺寸相等,填充(padding)的值应该等于多少?
答:填充的值=(7 - 1)/2 = 3,即每一边填充3层
对于 padding 的 same 和 valid 模式两种直白的理解就是要么不填充,要么填充就让输出尺寸和输入尺寸相等
padding 的值不会乱取,就两种情况,要么padding=0 要么padding=(b - 1)/2。
1)⽬标:对输⼊图像进⾏⼆次采样(即缩⼩),以减少计算量、内存占⽤和参数数量(减少过拟合⻛险)
2)特点:池化层中的每个神经元都和前⼀层神经元的输出相连,位于⼀个⼩的矩形感受区内。但池化神经元没有权重,它⽤聚合函数(如max_pool()或avg_pool())聚合输⼊。
3)作用:特征降维,避免过拟合;空间不变性;减少参数,降低训练难度
4)代码实现⼀个2x2内核的池化层(keras实现)
keras.layers.MaxPool2D( pool_size=(2,2), strides=None, #默认值和pool_size相等
padding='valid', data_format=None)
keras.layers.AvgPool2D(pool_size=(2,2),strides= None, # 默认值和pool_size相等
padding='valid', data_format=None)
keras.layers.GlobalAveragePooling2D(data_format=None)
import os
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D,
import numpy as np
(X_tarin, y_train), (X_test, y_test) = mnist.load_data()
X_train4D = X_tarin.reshape(X_tarin.shape[0], 28, 28, 1).astype('float32')
X_test4D = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32')
# 归⼀化
X_train4D_Normalize = X_train4D / 255
X_test4D_Normalize = X_test4D / 255
# 标签onehot编码
y_trainOnehot = to_categorical(y_train)
y_testOnehot = to_categorical(y_test)
# 建⽴模型
model = Sequential()
model.add(Conv2D(filters=16,kernel_size=(5, 5),padding='SAME', input_shape=(28, 28, 1),activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# ⼆层卷积
model.add(Conv2D(filters=32,kernel_size=(5, 5), padding='SAME', activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
#三、四层卷积
model.add(Conv2D(filters=64, kernel_size=(5, 5), padding='same', activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(filters=128, kernel_size=(5, 5), padding='same', activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 全连接层
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(10, activation='softmax'))
model.summary()
# 编译模型
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
#训练模型
train_history = model.fit(x=X_train4D_Normalize,y=y_trainOnehot,validation_split=0.2,batch_size=300,
epochs=10,verbose=2)
#评估模型
model.evaluate(X_test4D_Normalize, y_testOnehot)[1]
#预测
prediction = model.predict_classes(X_test4D_Normalize)
概念:⼀类⽤于处理序列数据的神经⽹络,⼀个序列当前的输出与前⾯的输出有关。具体的表现形式为:⽹络会对前⾯的信息进⾏记忆并应⽤于当前输出的计算中,即隐藏层之间的节点是有连接的,并且隐藏层的输⼊不仅包括输⼊层的输出还包括上⼀时刻隐藏层的输出。
参数3组权重:
激励函数 :⼀般为tanh或ReLU
o t o_t ot为时刻t的输出。 o t = s o f t m a x ( V s t ) o_t = softmax(V_{s_t}) ot=softmax(Vst)
分类:
简单RNN缺点:在预测⻓序列上表现不佳,⾯临不稳定梯度问题,易发生梯度消失或爆炸;且当 RNN 处理⻓序列时,会逐渐忘掉序列的第⼀个输⼊
np.random.seed(42)
tf.random.set_seed(42)
model = keras.models.Sequential([
keras.layers.SimpleRNN(1, input_shape=[None, 1]) ])
optimizer = keras.optimizers.Adam(learning_rate=0.005)
model.compile(loss="mse", optimizer=optimizer)
history = model.fit(X_train, y_train, epochs=20,validation_data=(X_valid, y_valid))
model.evaluate(X_valid, y_valid)
y_pred = model.predict(X_valid)
np.random.seed(42)
tf.random.set_seed(42)
model = keras.models.Sequential([
keras.layers.SimpleRNN(20, return_sequences=True,input_shape=[None, 1]),
keras.layers.SimpleRNN(20, return_sequences=True),
keras.layers.SimpleRNN(1)
])
model.compile(loss="mse", optimizer="adam")
history = model.fit(X_train, y_train, epochs=20,validation_data=(X_valid, y_valid))
model.evaluate(X_valid, y_valid)
y_pred = model.predict(X_valid)
np.random.seed(42)
tf.random.set_seed(42)
model = keras.models.Sequential([
keras.layers.SimpleRNN(20, return_sequences=True, input_shape=[None, 1])
keras.layers.BatchNormalization(),
keras.layers.SimpleRNN(20, return_sequences=True),
keras.layers.BatchNormalization(), 批量归一化
keras.layers.TimeDistributed(keras.layers.Dense(10)) 时间步
每⼀步都预测10个值,即在时刻0会预测1到10,时刻1预测2到11.
])
model.summary()
model.compile(loss="mse", optimizer="adam", metrics=[last_time_step_mse])
history = model.fit(X_train, Y_train, epochs=20, validation_data=(X_valid, Y_valid))
model.evaluate(X_valid, Y_valid)
c[t−1]先经遗忘⻔丢弃
⼀些记忆;后经添加操作增加
⼀些记忆(从输⼊⻔中选
择⼀些),最后不经转换直接输出。添加操作之后,⻓时状态复制
后经tanh 激活函数,后结果被输出⻔过滤
,得到短时状态h[t]。h[t]即为这⼀时间步的单元输出y[t]。
短期记忆状态 h [ t − 1 ] h[t−1] h[t−1]和输⼊向量 x [ t ] x[t] x[t]经4个不同的全连接层: g [ t ] g[t] g[t]层为主要层(将最重要的部分保存在⻓期状态中,tanh为激活函数),其它三个全连接层(FC)是⻔控制器
(Logistic作为激活函数)
权重计算
W x i , W x f , W x o , W x g W_{xi},W_{xf} ,W_{xo},W_{xg} Wxi,Wxf,Wxo,Wxg是四个全连接层连接输⼊向量x(t)的权重;
W h i , W h f , W h o , W h g W_{hi},W_{hf} ,W_{ho},W_{hg} Whi,Whf,Who,Whg是四个全连接层连接h(t−1)的权重;
b i , b f , b o , b g b_i,b_f ,b_o,b_g bi,bf,bo,bg是全连接层的四个偏置项。
W x i , W x f , W x o , W x g W_{xi},W_{xf} ,W_{xo},W_{xg} Wxi,Wxf,Wxo,Wxg 权重数量:
输入参数数量×隐层参数数量+隐层参数数量 ×隐层参数数量
偏置数量:隐层参数数量
np.random.seed(42)
tf.random.set_seed(42)
model = keras.models.Sequential([
keras.layers.LSTM(20, return_sequences=True, input_shape=[None, 1]),
keras.layers.LSTM(20, return_sequences=True),
keras.layers.TimeDistributed(keras.layers.Dense(10))
])
model.summary()
model.compile(loss="mse", optimizer="adam", metrics=[last_time_step_mse])
history = model.fit(X_train, Y_train, epochs=20, validation_data=(X_valid, Y_valid))
model.evaluate(X_valid, Y_valid)
⻓时状态和短时状态合并为⼀个向量h(t)。⽤⼀个⻔控制器z(t)(重置门)控制遗忘⻔和输⼊⻔
增加⼀个控制门r(t)(更新门)来控制前⼀状态的哪些部分呈现给主层g(t)
应对⽅法之⼀是将输⼊序列缩短,例如使⽤ 1D 卷积层。1D 卷积层在序列上滑动⼏个核,每个核可以产⽣⼀个 1D 特征映射。 每个核能学到⼀个⾮常短序列模式(不会超过核的⼤⼩)
np.random.seed(42)
tf.random.set_seed(42)
model = keras.models.Sequential([
keras.layers.GRU(20, return_sequences=True, input_shape=[None, 1]),
keras.layers.GRU(20, return_sequences=True),
keras.layers.TimeDistributed(keras.layers.Dense(10))
])
model.summary()
model.compile(loss="mse", optimizer="adam", metrics=[last_time_step_mse])
history = model.fit(X_train, Y_train, epochs=20, validation_data=(X_valid, Y_valid))
import torch
import torch.nn as nn
import torchvision.datasets as ds
import torchvision.transforms as transforms
from torch.autograd import Variable
sequence_length = 28 , input_size = 28 , hidden_size = 128
num_layers = 2, num_classes = 10 , batch_size = 100 , num_epochs = 2
learning_rate = 0.003
#加载MNIST数据集
train_dataset = ds.MNIST(root='.\data',train=True,transform=transforms.ToTensor(),download=True)
test_dataset = ds.MNIST(root='.\data',train=False,transform=transforms.ToTensor())
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=False)
#定义多对⼀双向RNN模型
class BiRNN(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, num_classes):
super(BiRNN, self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
self.lstm = nn.LSTM(input_size, hidden_size, num_layers,
batch_first=True, bidirectional=True)
self.fc = nn.Linear(hidden_size * 2, num_classes)
def forward(self, x):
h0 = Variable(torch.zeros(self.num_layers * 2, x.size(0),self.hidden_size)) # 设置初始状态
c0 = Variable(torch.zeros(self.num_layers * 2, x.size(0),self.hidden_size))
out, _ = self.lstm(x, (h0, c0)) #前向传播
out = self.fc(out[:, -1, :]) # 上一时间步隐藏状态输出
return out
#定义模型
rnn = BiRNN(input_size, hidden_size, num_layers, num_classes)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(rnn.parameters(), lr=learning_rate)
#训练模型
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
images = Variable(images.view(-1, sequence_length, input_size))
labels = Variable(labels)
optimizer.zero_grad()#前向+后向+优化器
outputs = rnn(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
if (i + 1) % 100 == 0:
print('Epoch [%d/%d], Step [%d/%d], Loss: %.4f'% (epoch+1,num_epochs,i + 1, len(train_dataset)
// batch_size, loss.item()))
# 测试模型
correct = 0
total = 0
for images, labels in test_loader:
images = Variable(images.view(-1, sequence_length, input_size))
outputs = rnn(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted.cpu() == labels).sum()
print('Test Accuracy of the model on test images: %d %%' % (100 * correct / total))
X_train = np.random.rand(100,3)
encoder = Sequential([Dense(2, input_shape=[3])])
decoder = Sequential([Dense(3)])
autoencoder = Sequential([encoder, decoder])
autoencoder.compile(loss="mse", optimizer=SGD(lr=0.1))
history = autoencoder.fit(X_train, X_train, epochs=20)
codings = encoder.predict(X_train)
自动编码器被迫学习输⼊数据中最重要的特征,并且要删除不重要的特征。(借助PCA)如果⾃动编码器仅使⽤线性激活并且损失函数是均⽅误差(MSE),最终其实是做了主成分分析(PCA)
⾃动编码器可以有多个隐藏层,称为深度⾃动编码器。
添加更多层有助于⾃动编码器学习更复杂的编码。 但不要让⾃动编码器功能太强⼤。
如果⼀个编码器太强⼤,就会导致过拟合问题, 这样的⾃动编码器将完美地重构训练数据,但它不会在过程中学习到任何有⽤的数据表征,并且它不可能很好地泛化到新的实例。
import utils.mnist_reader
X_train, y_train = utils.mnist_reader.load_mnist('./data/', kind='train')
X_valid, y_valid = utils.mnist_reader.load_mnist('./data/', kind='t10k')
X_train = X_train.reshape(X_train.shape[0],28,28).astype('float32')
X_valid = X_valid.reshape(X_valid.shape[0],28,28).astype('float32')
stacked_encoder = Sequential([
Flatten(input_shape=[28, 28]),
Dense(100, activation="selu"),
Dense(30, activation="selu"),
])
stacked_decoder = Sequential([
Dense(100, activation="selu", input_shape=[30]),
Dense(28 * 28, activation="sigmoid"),
Reshape([28, 28])
])
stacked_ae = Sequential([stacked_encoder, stacked_decoder])
stacked_ae.compile( loss="binary_crossentropy", optimizer=SGD(lr=1.5))
history = stacked_ae.fit(X_train,X_train,epochs=10,validation_data=[X_valid])
卷积⾃动编码器通常会降低输⼊的空间维度(即,⾼和宽),同时增加深度(即,特征映射的数量)。⽽解码器的⼯作相反,即放⼤图⽚,压缩深度。可以通过转置卷积层实现。也可以将上采样层和卷积层合并。
conv_encoder = Sequential([
Reshape([28, 28, 1], input_shape=[28, 28]),
Conv2D(16, kernel_size=3, padding="same", activation="selu"),
MaxPool2D(pool_size=2),
Conv2D(32, kernel_size=3, padding="same", activation="selu"),
MaxPool2D(pool_size=2),
Conv2D(64, kernel_size=3, padding="same", activation="selu"),
MaxPool2D(pool_size=2)
])
conv_decoder = Sequential([
Conv2DTranspose(32, kernel_size=3, strides=2, padding="valid",
activation="selu", input_shape=[3, 3, 64]),
Conv2DTranspose(16, kernel_size=3, strides=2, padding="same",
activation="selu"),
Conv2DTranspose(1, kernel_size=3, strides=2, padding="same",
activation="sigmoid"),
Reshape([28, 28])
])
conv_ae = Sequential([conv_encoder, conv_decoder])
反卷积:
⼀种⽤于扩⼤图像的尺⼨的⽅法,称为逆卷积或反卷积。
例如:输⼊特征图:3 ∗ 3 输⼊卷积核:kernel=3 ∗ 3, stride=2,padding=1
输出特征图:3 * 3 - 3 + 2 * 1 +1 = 5
循环自动编码器
循环⾃动编码器中的编码器是⼀个序列到向量的 RNN,⽽解码器是向量到序列的 RNN。
recurrent_encoder = Sequential([
LSTM(100, return_sequences=True, input_shape=[None, 28]),
LSTM(30)
])
recurrent_decoder = Sequential([
RepeatVector(28, input_shape=[30]), 将输⼊重复28次
LSTM(100, return_sequences=True),
TimeDistributed(Dense(28, activation="sigmoid"))将Dense层应⽤于输⼊的每个时间⽚(对输入的每个向量进行一次Dense操作)。
])
recurrent_ae = Sequential([recurrent_encoder, recurrent_decoder])
损失函数:
变分⾃动编码器不是直接为给定的输⼊⽣成编码;编码器产⽣平均编码μ和标准差σ, 然后从平均值μ和标准差σ的⾼斯分布随机采样实现编码。之后,解码器正常解码采样的编码。
Agent:智能体,也就是机器人,你的代码。
Environment:环境,也就是游戏本身,openai gym提供了多款游戏,也就是提供了多个游戏环境。
Action:行动,比如玩超级玛丽,向上向下等动作。
State:状态,每次智能体做出行动,环境会相应地做出反应,返回一个状态和奖励。
Reward:奖励:根据游戏规则的得分。智能体不知道怎么才能得分,它通过不断地尝试来理解游戏规则,比如它在这个状态做出向上的动作,得分,那么下一次它处于这个环境状态,就倾向于做出向上的动作。
在强化学习中,智能体(Agent)在环境(Environment)中观察(Observation)状态(State)并且做出决策(Action),随后它会得到奖励(Reward)。
gym的核心接口是统一的环境Env,Env包含下面几个核心方法:
1、reset(self)
:重置环境的状态,返回观察值 observation 。
2、step(self,action)
:推进一个时间步长,返回[observation,reward,done,info]
3、render(self,mode='human', close=False)
: 重绘环境的一帧。默认模式一般比较友好,如弹出一个窗口。
4、info(dict)
:用于调试的诊断信息。
5、observation(object)
:返回一个特定环境的对象,描述对环境的观察。比如,来自相机的像素数 据,机器人的关节角度和关节速度,或棋盘游戏中 的棋盘状态。
6、reward(float)
:返回之前动作收获的总的奖 励值。
7、done(boolean)
:返回是否应该重新设置 (reset)环境。大多数游戏任务分为多个环节 (episode),当done=true的时候,表示这个环节结束了。
对于 CartPole 环境,每个观测是包含四个浮点的 1D Numpy 向量:
1.小车的水平位置(0 为中心)
2.小车的速度
3.杆的角度(垂直)
4.杆的角速度
CartPole-v0示例
env = gym.make('CartPole-v0') # 初始化环境
for i_episode in range(20): # 外层循环
obs = env.reset() 返回观察值
for t in range(100): # 内层循环
env.render() 重绘环境的一帧。
action = env.action_space.sample() 随机选取动作
obs, re, done, info = env.step(action)
print(obs)
if done:
print("done == True : %d" % (t+1))
break
env.close()
在强化学习中,被智能体(Agent)使用去改变它行为的算法叫做策略(Policy)。例如,策略可以是一个把观测(Observations)当输入,行为(Actions)当做输出的神经网络。
搜寻策略方法:
1)随机策略:这个策略可以是你能想到的任何算法;
2)尝试策略:可以通过尝试来搜索,即通过大量实验来选择参数的最佳组合;
3)遗传算法:通过遗传算法来搜索最佳参数
4)策略梯度(PG):使用优化技术,通过评估奖励关于策略参数的梯度,然后通过跟随梯度向更高的奖励(梯度上升)调整这些参数;
5)神经网络策略:神经网络将把观察作为输入,输出要执行的动作。它将估计每个动作的概率,然后我们将根据估计的概率随机地选择一个动作。
Q:为什么根据神经网络给出的概率来选择随机的动作,而不是选择最高分数的动作?
A:这种方法使智能体在探索新的行为和选择可行的行动之间找到平衡。
在强化学习中,智能体获得的指导的唯一途径是通过奖励,奖励通常是稀疏的和有延迟的。
信用分配问题:当智能体得到奖励时,如何知道哪些行为应该被信任(或责备)。
解决策略:是基于这个动作后得分的总和来评估这个动作,通常在每个步骤中应用衰减率r。如果衰减率接近0,那么与即时奖励相比,未来的奖励不会有多大意义。相反,如果衰减率接近1,那么对未来的奖励几乎等于即时回报。
如果一个智能体决定连续三次向右,在第一步之后得到 +10 奖励,第二步后得到 0,第三步之后得到 -50,衰减率r=0.8,那么第一个动作将得到 10 + r × 0 + r 2 × ( − 50 ) = 10 + 0.8 × 0 + 0. 8 2 × ( − 50 ) = − 22 10+ r ×0+ r^2 ×(−50) = 10+0.8×0+0.8^2 ×(−50)=−22 10+r×0+r2×(−50)=10+0.8×0+0.82×(−50)=−22的分数。
Q值迭代算法:
动态规划的一个例子,它将一个复杂的问题分解为易处理的子问题,并且可以通过迭代解决。
原理:将状态-动作值 (s,a) 称为Q值,记为Q(s,a),它将返回在该状态下执行该动作的未来奖励期望。状态-动作(s,a)的最佳Q值,记为Q*(s,a)。
Q-Learning算法是Q值迭代算法适应转移概率和奖励未知的情况。
对于每个状态 - 动作对(s,a),算法跟踪agent 在离开状态s时采取动作a的奖励的运行平均值,以及预期稍后获得的奖励。
由于目标策略将采取最优行动,因此我们对下一个状态采取最大Q值估计。
估值Q-Learning
Q-Learning的主要问题是它不能很好地扩展到具有许多状态和动作的大型、中型MDP问题。找到一个使用参数数量可管理的函数来近似Q值,称为估值Q-Learning
用一个简单的例子介绍Q-Learning算法的执行过程。在一幢建筑里有6个房间,房间用编号0-5表示,房间之间通过门相连。房间5是建筑外围空间,可以看作是一个大的房间。首先将Agent置于建筑中任意一个房间,其1目标是从那个房间开始走到房间5。
Step 1 给定参数 和R矩阵,Q=0.
Step 2 循环执行Step3,4
Step 3 随机选择一个初始状态s
Step 4 循环执行:
1.判断s是否是目标状态,如否则执行下列步骤,如是则结束Step 4.
2.在当前状态s的所有可能行动中选取一个动作a
3.利用选定的动作a,得到下一个状态s~
4.利用公式计算Q(s,a)
5.令s=s~
执行过程
循环1
令初始状态为1。
状态不为5,则在R矩阵中选择状态1的可能动作:3或5。
选择状态5,得到下一个状态为5。
利用公式计算Q(1,5)
Q矩阵更新为:
当前状态为5,所以下次循环判断后即可结束
循环2
回到Step 3, 重新选择一个初始状态。
选择初始状态为3.
找出状态3的可能动作:1,2,4
选择状态1,对应2个可能的动作:3或5
利用公式计算Q(3,1)
Q矩阵更新为:
现在的当前状态变成了状态1,因为状态1不是目标状态,因此从状态1 的可能动作选择一个:3或5。
选择状态5,其动作有3个可能:1,4或5;
利用公式计算Q(1,5)
继续循环,继续执行多次后,将得到矩阵Q:
一旦Q接近收敛状态,就可以指导Agent进行最佳路径选择。
如,假定Agent从状态2出发,可到到下列路径:
1. Q矩阵中,状态2的最大元素值为320,选择状态3;
2. 状态3的最大元素值为400, 状态为1或4,选择状态4;
3. 状态4的最大元素值为500,即状态5,到达目标。
因此最佳路径为:2-3-4-5