神经网络
有监督的算法。
神经网络算法既能做回归又能做分类。
我们说人工智能做的就是拟人,神经网络就做的最佳的拟人。
1.神经元介绍:
神经网络是由很多神经元组成。神经元的每个输入是机器学习中的每个特征维度X,每个连接是权重W,将特征维度和权重相乘再相加汇总,经过一个非线性变换(函数变换)后的结果,就是一个神经元的输出。也是下层神经元的输入。
也就是说对于一个神经元,有可能就是一个线性回归,也有可能就是一个逻辑回归,这取决于加和后的非线性变换是什么函数。
那么整个神经网路就是每一个浅层的机器学习算法的组合。
2.激活函数(activition function):
在神经元中引入了激活函数,它的本质事项神经网络中引入非线性因素,通过激活函数,神经网络可以拟合各种曲线。通过引入激活函数那输出不再是线性组合,可以逼近任意函数。
将数据与权重相乘再加和汇总后,经过一个非线性变换。这个非线性变换就是激活函数。三种常见的激活函数,激活函数一般用在隐藏层,输出层一般叫做非线性变换。
(1)sigmoid
主要用于二分类。将数据缩放到0-1之间
(2)tanh将数据缩放到(-1,1)之间(在RNN循环神经网络隐藏层里面用的比较多)
(3)Relu:现在用relu的比较多。在(0,x)之间取值,大于0就去当前值,小于0就取0.
(4)LeakReLu:Relu激活函数的改进,大量的Relu激活函数可能会导致神经元死亡的现象,LeakReLu就是为了避免这样的情况发生。(类似于加了一个平滑系数)
当用Relu效果不好的时候,可以尝试使用LeakRelu激活函数。
(5)sofamax:用于多分类的输出层中,主要用于多分类。有多个概率结果,这些结果加和等于1,其中概率最大的就是我们要预测的类别。
如何选择激活函数
神经元初始化参数
对于一个神经元来说,需要初始化的参数有两类,一类是权重,一类是偏置b(初始化0即可)
初始化方式:
1.随机初始化:均值为零,标准差为1的正态分布随机取值初始化方式(很少用)。
2.标准初始化:假设有n个输入,那么就根据均匀分布从-1导根号下n之间均匀取值(很少用)。
3.Xavier初始化:
Glorot正态分布初始化器,是以0为中心,标准差为stddev=sqrt(2 / fan_in + fan_out)的正态分布抽样样本,fan_in输入神经元个数,fan_out输出神经元个数。
init = tf.keras.initializers.glorot_normal()
# 采样得到的数量9行1列
values = init(shape=(9, 1))
print(values)
Glorot均匀分布初始化器,是[-limit,limit]均匀分布中抽取样本,其中limit是sqrt(6/fan_in + fan_out)
init2 = tf.keras.initializers.glorot_uniform()
values = init2(shape=(9, 1))
print(values)
4.he初始化:
基本思想:正向传播时,激活值的方差保持不变 ,反向传播时,状态值梯度方差保持不变,
he正态分布初始化以0为中心,标准差为stddev=sqrt(2 / fan_in)
he标准化初始化是[-limit,limit]均匀分布中抽取样本,其中limit是sqrt(6/fan_in)
init3 = tf.keras.initializers.he_normal()
values = init3(shape=(9, 1))
print(values)
init4 = tf.keras.initializers.he_uniform()
values = init4(shape=(9, 1))
print(values)
常见的损失函数
损失函数就是衡量预测值与真实值之间的差异。真实值与预测值之间的差异越小,交叉熵损失越小。
1.多分类损失函数:交叉熵损失函数
y_true = [[0, 1, 0], [0, 0, 1]]
y_pred = [[0.05, 0.95, 0], [0.1, 0.1, 0.8]]
cce = tf.keras.losses.CategoricalCrossentropy()
print(cce(y_true, y_pred))
2.二分类损失函数:二分类交叉熵损失函数
y_ture = [[0], [1]]
y_pred = [[0.4], [0.6]]
bce = tf.keras.losses.BinaryCrossentropy()
print(bce(y_true, y_pred))
3.回归任务损失函数MAE最小绝对误差,MSE最小均方误差,smoothL1损失
MAE(L1)损失函数在0点处不可导,所以用梯度下降不合适,一般用于L1正则化项加载损失函数后面。
y_true = [[0.], [1.]]
y_pred = [[1.], [0.]]
bce = tf.keras.losses.MeanAbsoluteError()
print(bce(y_true, y_pred))
MSE最小均方误差(L2),一般当作L2正则项使用,使用的话容易出现梯度爆炸。
y_true = [[0.], [1.]]
y_pred = [[1.], [0.]]
bce = tf.keras.losses.MeanSquaredError()
print(bce(y_true, y_pred))
smoothL1损失,是一个分段函数,在[-1, 1]之间实际上是L2损失 ,解决了L1不光滑0点不可导的问题,在[-1,1]区间外实际上就是L1损失,解决了离群点梯度爆炸的问题。
y_true = [[0.], [1.]]
y_pred = [[1.], [0.]]
bce = tf.keras.losses.Huber()
print(bce(y_true, y_pred))
3.网络拓扑:
神经元数量以及层数和他们的连接方式(DNN全连接,CNN局部链接)
单层神经网络
输入层:对于输入层我们要有X特征,y标签
输入层的数据假设有三个特征维度,那么输入层的第一条样本的三个数值输入与x1,x2,x3 进行相乘并汇总加和。
输出层:汇总加和的值经过function变换后就是输出。
4.单层神经网络逻辑回归多分类二分类
如果最后的function是sigmoid,那最后输出的值是概率。
如果输入层是批量输入,假设一次输入了三条样本,那么X1,X2,X3都是三个值,再与w1,w2,w3相乘加和后(注意这里加和是每条样本分别加和并不是所有都加在一起,例如第一条样本X11*w1+X12*w2+X13*w3,第二条样本X12*w1+X22*w2+X32*w3,第三条样本X13*w1+X23*w2+X33*w3)经过function变换得到y1,y2,y3
对于逻辑回归来说,如果做二分类,单层神经网络就会有一个输出节点(一个正例的概率,1-p得到负例的概率)
逻辑回归做多分类(OVR)就会有多个输出节点。比如做三分类就会有三个输出节点(三个类别的概率,也会有三个训练模型),在逻辑回归求梯度调整W参数的时候,每个输出的节点都会调整自己的W参数,不会调整别的W,这一点和softmax有本质差别。
5.单层神经网络softmax做多分类
softmax每个类别概率加在一起等于一
交叉熵损失函数 =
由公式我们可以看出求出来的概率不仅要用到当前类别的的z,还要用到其他类别的z作为分母加和。
我们知道对于softmax真实值yij,如果有三个类别,其中真实值是第二个类别那么真实值就是 (0,1,0),有交叉熵损失函数,也就是说最终求得预测结果只有第二个值对loss有贡献。然后在反向求梯度。
但是尽管只有第二个对loss有贡献,他要调整的参数θ也是所有的θ,因为softmax公式决定了每个类别的概率是所有的的加和。
6.网络拓扑层多一层网络
(1)输入层-->隐藏层-->输出层,隐藏层可以有多层。隐藏层的激活函数必须是非线性的激活函数。
(2)隐藏层的意义:可以抽取更高阶的特征,让结果更准确。
(3)隐藏层越多,当然预测也会越准确,但是也意味着中间有更多的参数要去学习。也有可能过拟合。
(4)隐藏层每一层都要加上截距项。
神经网络每一层的神经元数量是可以由我们决定的,所以隐藏如果下一层神经元数量比上一层神经元数量少,可以起到降维的作用。相反,下一层神经元比上一层数量多,又可以起到升维的作用。
所以神经网络可已经数据预处理和求解参数当作一个整体。包括预处理也是需要调整和训练的,而机器学习是将数据预处理和训练分成两个阶段。
7.多节点输出网络
二分类有一个输出节点
多分类有多个输出节点
做回归也可以有多个输出节点
其实输出层也可以同时做回归和分类。
8.神经网络有两个阶段:
正向传播:从输入经过隐藏层到输出层,这个过程叫做正向传播。正向传播要计算y_hat
反向传播(Backpropagation)
反向传播我们要求出梯度,所以如果loss对Wj求偏导,但是W有对y_hat有影响,所以根据链式求导法则,
在前向传播后向传播过程中有求导的过程,我们用梯度下降法进行求导
9.使用tensorflow.keras构建神经
单个网络层构建方式
"""
activation激活函数
use_bias截距项
bias_initializer截距初始化
kernel_initializer参数初始化方式
"""
tf.keras.layers.Dense(units=True, activation=None, use_bias=True,
kernel_initializer='glorot_uniform', bias_initializer='zero')
1.代码通过Sequential()快速构建一个神经网络模型,只能构建简单的网络结构。
# sequential方式
model = tf.keras.Sequential([
# 输入层
tf.keras.layers.Dense(3, activation="relu", kernel_initializer="he_normal", name="layer1", input_shape=(3,)),
# 隐藏层
tf.keras.layers.Dense(2, activation="relu", kernel_initializer="he_normal", name="layer2"),
# 输出层
tf.keras.layers.Dense(2, activation="sigmoid", kernel_initializer="he_normal", name="layer3")
],
name="sequ"
)
# 展示模型结果
print(model.summary())
tf.keras.utils.plot_model(model)
2.通过函数式编程构建神经网络(用的最多)
# 函数式编程实现神经网络
# 定义输入层
inputs = tf.keras.Input(shape=(3,), name="input")
# 定义layer1
x = tf.keras.layers.Dense(3, activation="relu", name="layer1")(inputs)
# 定义layer2
x = tf.keras.layers.Dense(2, activation="relu", name="layer2")(x)
# 定义输出层
outputs = tf.keras.layers.Dense(2, activation="sigmoid", name="output")(x)
# 使用Model定义模型,指明输入和输出
model = tf.keras.Model(inputs=inputs, outputs=outputs, name="my_model")
# 展示模型结果
print(model.summary())
# show_shapes=True打印当前维度
tf.keras.utils.plot_model(model, show_shapes=True)
3.通过model子类构建神经网络
# 基于model子类实现神经网络
class MyModel(tf.keras.Model):
def __init__(self):
super(MyModel, self).__init__()
self.layer1 = tf.keras.layers.Dense(3, activation="relu", kernel_initializer="he_normal", name="layer1", input_shape=(3,))
self.layer2 = tf.keras.layers.Dense(2, activation="relu", kernel_initializer="he_normal", name="layer2")
self.layer3 = tf.keras.layers.Dense(2, activation="relu", kernel_initializer="he_normal", name="layer3")
def call(self, inputs):
x = self.layer1(inputs)
x = self.layer2(x)
return self.layer3(x)
# 实例化
print("-------------------------")
model = MyModel()
print(model(tf.ones((1, 3))))
model.summary()
10.神经网络中的梯度下降算法,深度学习中SGD是直接用的小批量梯度下降。
opt = tf.keras.optimizers.SGD(learning_rate=0.1)
var = tf.Variable(1.0)
# 定义损失函数
loss = lambda:(var**2)/2.0
# 计算梯度,并进行参数更新,这一步就是把所有参数带入梯度下降公式进行计算
opt.minimize(loss, [var])
# 参数更新结果
print(var.numpy())
# 这里使用梯度下降法公式Wt = W(t-1) - 学习率*梯度
# 学习率已知,梯度是对自己定义的loss损失函数求导得到var
# 那么结果就是1.0 - 0.1*1.0 = 0.9
11.神经网络的优缺点
优点:
精度高,性能优于其他机器学习方法
可以近似任意非线性函数
随着计算机硬件的发展,近年来在学界和业界收到热捧,有大量的框架和库可供调用
缺点:
黑箱,很难解释模型是怎样工作的
训练时间长,需要大量的算力
网络结构复杂,需要调整超参数
小数据表现不佳,容易发生过拟合