四、神经网络(深度学习算法)
4.1 认识神经网络
- 必要性
- 当特征值只有两个时,我们仍可以用之前学过的算法去解决

- 但当特征值很多,且含有很多个多次多项式时,用之前的算法就很难解决了
例子 :图像感知 Recogonition image
计算机识别汽车是靠像素点的亮度值 -

神经网络做法:

4.2 如何在神经网络上推理
4.2.1 神经网络定义
定义一个神经网络如下:

计算第l层第j个激活值的公式:

注意符号的含义 w2_1 上标2代表layer 2 下标 1代表第一个神经元
4.2.2 构建:前向传播 forward propagation(使用TensorFlow实现)
前向传播是从输入层开始,通过各层计算直至输出层,从而得出预测结果。
- eg1:手写识别0、1
-

- 每次层计算, 从左往右计算,要传播神经元的激活值

-
- eg2:烤咖啡的好坏

- python实现前向传播

自定义dense密集函数:输入前一层的激活,给定当前层的参数,它会输出下一层激活值 自定义sequential函数
其中:W大写在线性代数中代表矩阵
- 拓展: AGI 通用人工智能
4.3 课后作业
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.io import loadmat
from scipy.optimize import minimize
# 加载数据集,这里的数据为matlab格式,所有要用SciPy.io的loadmat函数
def load_data(path):
data = loadmat(path)
X = data['X']
y = data['y']
return X, y
X, y = load_data('ex3data1.mat')
print(np.unique(y)) # 找到数组 y 中的所有唯一元素,并返回一个已排序的数组
# [ 1 2 3 4 5 6 7 8 9 10]
X.shape, y.shape
# ((5000, 400), (5000, 1))
# 其中有5000个训练样本,每个样本是20 * 20
# 像素的数字的灰度图像。每个像素代表一个浮点数,表示该位置的灰度强度。20×20的像素网格被展开成一个400维的向量。
# 在我们的数据矩阵X中,每一个样本都变成了一行,这给了我们一个5000×400矩阵X,每一行都是一个手写数字图像的训练样本。
# 第一个任务是将我们的逻辑回归实现修改为完全向量化(即没有“for”循环)。
# 这是因为向量化代码除了简洁外,还能够利用线性代数优化,并且通常比迭代代码快得多。
# Visualizing the data
def plot_an_image(X):
"""
随机打印一个数字
"""
pick_one = np.random.randint(0, 5000) # 从0到5000中随机选取一个整数
image = X[pick_one, :] # 从数据集中选取这个随机索引对应的图像数据
fig, ax = plt.subplots(figsize=(1, 1))
# 将选取的图像数据重新塑形为20x20的矩阵,并使用灰度反转的颜色显示
ax.matshow(image.reshape((20, 20)), cmap='gray_r') # matshow用于以矩阵形式显示二维数组
plt.xticks([])
plt.yticks([]) # 去除x轴和y轴的刻度,使图像显示更美观。
plt.show()
print('this should be {}'.format(y[pick_one]))
def plot_100_image(X):
"""
随机画100个数字
"""
sample_idx = np.random.choice(np.arange(X.shape[0]), 100)
sample_images = X[sample_idx, :] # (100, 400)
# 创建一个10x10的子图网格,共享x轴和y轴的刻度和标签,图像大小为8x8英寸
fig, ax_array = plt.subplots(nrows=10, ncols=10, sharey=True, sharex=True, figsize=(8, 8))
for row in range(10):
for column in range(10):
ax_array[row, column].matshow(sample_images[10 * row + column].reshape((20, 20)), cmap='gray_r')
plt.xticks([])
plt.yticks([])
plt.show()
# Vectorizing Logistic Regression
# 我们将使用多个one-vs-all(一对多)logistic回归模型来构建一个多类分类器。由于有10个类,需要训练10个独立的分类器。
# 为了提高训练效率,重要的是向量化。
# Vectorizing the cost function
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def regularized_cost(theta, X, y, l):
"""
don't penalize theta_0
args:
X: feature matrix, (m, n+1) # 插入了x0=1
y: target vector, (m, )
l(正则化常数λ): lambda constant for regularization
"""
thetaReg = theta[1:]
first = (-y*np.log(sigmoid(X@theta))) + (y-1)*np.log(1-sigmoid(X@theta))
reg = (thetaReg@thetaReg)*l / (2*len(X))
return np.mean(first) + reg
# Vectorizing the gradient
def regularized_gradient(theta, X, y, l):
"""
don't penalize theta_0
args:
l: lambda constant
return:
a vector of gradient
"""
thetaReg = theta[1:]
first = (1 / len(X)) * X.T @ (sigmoid(X @ theta) - y)
# 这里人为插入一维0,使得对theta_0不惩罚,方便计算
reg = np.concatenate([np.array([0]), (l / len(X)) * thetaReg]) # 使用np.concatenate将上面两个数组连接在一起。
return first + reg
# One-vs-all Classification
# 这部分我们将实现一对多分类通过训练多个正则化logistic回归分类器,每个对应数据集中K类中的一个。
# 对于这个任务,我们有10个可能的类,并且由于logistic回归只能一次在2个类之间进行分类,每个分类器在“类别i”和“不是i”之间决定。
# 我们将把分类器训练包含在一个函数中,该函数计算10个分类器中的每个分类器的最终权重,并将权重返回shape为(k,(n + 1))数组,其中n是参数数量。
def one_vs_all(X, y, l, K):
"""generalized logistic regression
args:
X: feature matrix, (m, n+1) # with incercept x0=1
y: target vector, (m, )
l: lambda constant for regularization λ控制正则化强度
K: numbel of labels
return: trained parameters
"""
all_theta = np.zeros((K, X.shape[1])) # (10, 401)
for i in range(1, K+1):
theta = np.zeros(X.shape[1])
y_i = np.array([1 if label == i else 0 for label in y])
ret = minimize(fun=regularized_cost, x0=theta, args=(X, y_i, l), method='TNC',
jac=regularized_gradient, options={'disp': True}) # 设置disp为True以显示优化过程。
all_theta[i - 1, :] = ret.x
return all_theta
# 这里需要注意的几点:首先,我们为X添加了一列常数项1 ,以计算截距项(常数项)。
# 其次,我们将y从类标签转换为每个分类器的二进制值(要么是类i,要么不是类i)。 最后,我们使用SciPy的较新优化API来最小化每个分类器的代价函数。
# 如果指定的话,API将采用目标函数,初始参数集,优化方法和jacobian(渐变)函数。 然后将优化程序找到的参数分配给参数数组。
# 实现向量化代码的一个更具挑战性的部分是正确地写入所有的矩阵,保证维度正确。
def predict_all(X, all_theta):
# 计算每个样本在每个类别上的概率
h = sigmoid(X @ all_theta.T) # 注意的这里的all_theta需要转置
# 创建一个数组,包含每个样本概率最大的类别的索引
# axis=1,返回沿着每行或水平方向最大值的索引
h_argmax = np.argmax(h, axis=1)
# 将索引值从0开始的数组转换为从1开始的数组。
h_argmax = h_argmax + 1
return h_argmax
# 这里的h共5000行,10列,每行代表一个样本,每列是预测对应数字的概率。我们取概率最大对应的index加1就是我们分类器最终预测出来的类别。
# 返回的h_argmax是一个array,包含5000个样本对应的预测值。
raw_X, raw_y = load_data('ex3data1.mat')
X = np.insert(raw_X, 0, 1, axis=1) # (5000, 401)
y = raw_y.flatten() # 这里消除了一个维度,将其转换为一维数组,方便后面的计算 or .reshape(-1) (5000,)
all_theta = one_vs_all(X, y, 1, 10)
all_theta # 每一行是一个分类器的一组参数
y_pred = predict_all(X, all_theta)
accuracy = np.mean(y_pred == y)
print('accuracy = {0}%'.format(accuracy * 100))
# Neural Networks
# 上面使用了多类logistic回归,然而logistic回归不能形成更复杂的假设,因为它只是一个线性分类器。
# 接下来我们用神经网络来尝试下,神经网络可以实现非常复杂的非线性的模型。我们将利用已经训练好了的权重进行预测。
def load_weight(path):
data = loadmat(path)
return data['Theta1'], data['Theta2']
theta1, theta2 = load_weight('ex3weights.mat')
# theta1.shape, theta2.shape
# 因此在数据加载函数中,原始数据做了转置,然而,转置的数据与给定的参数不兼容,因为这些参数是由原始数据训练的。
# 所以为了应用给定的参数,需要使用原始数据(不转置)
X, y = load_data('ex3data1.mat')
y = y.flatten()
# 向特征矩阵 X 的每一行开头插入一个值为1的列
X = np.insert(X, 0, values=np.ones(X.shape[0]), axis=1)
X.shape, y.shape
a1 = X
z2 = a1 @ theta1.T
# z2.shape
z2 = np.insert(z2, 0, 1, axis=1)
a2 = sigmoid(z2)
# a2.shape
z3 = a2 @ theta2.T
# z3.shape
a3 = sigmoid(z3)
# a3.shape
y_pred = np.argmax(a3, axis=1) + 1
accuracy = np.mean(y_pred == y)
print ('accuracy = {0}%'.format(accuracy * 100)) # accuracy = 97.52%
4.4 训练神经网络
4.4.1 模型训练细节
- eg:对于01手写分类问题

- 对于逻辑回归、二分类问题用Binary Cross Entropy二元交叉熵损失函数

使用二元交叉熵作为损失函数时,我们通常使用以下公式来计算单个样本的损失:loss = -[ylog(p) + (1-y)log[(1-p)]其中,y是真实标签(0或1),p是预测值(0到之间的概率值)。这个公式表示当真实标签为1时,我们希望预测值越接近1,此时损失越小,等于-log(p);当真实标签为0时,我们希望预测值p越接近0,此时损失也越小,等于-log(1-p)。
4.4.2 如何选择激活函数
- 三种基础激活函数
- 对于输出层激活函数的选择,取决于输出的y的取值范围
- hidden layer 隐藏层
- 除非是二分类问题用sigmoid,一般默认用relu,不能用线性激活函数,否则不能拟合,就相当于一个线性回归了
- relu计算很快,并且只有右边扁平flat,而sigmoid有两处flat,会影响gd的速度
4.5 多分类问题
4.5.1 softmax 回归算法
- Softmax 回归(Softmax Regression)是一种广义的逻辑回归,用于多分类问题。它可以看作是二分类逻辑回归的扩展,能够同时处理多个类别。Softmax 回归通常用于神经网络的输出层,将输出值转换为概率分布。
- Softmax 回归算法的模型(参考)
- 输出层计算方式

输出层也叫作softmax层,输出的K维向量a,中的每一个分量都与z1-z10有关系,这个和之前学的不太一样
4.5.2 多标签分类问题

输出值y是一个向量,而不是一个数,不能用softmax,因为比如既有人又有车(每个样本可以有多个类别标签)无法处理,对每个分量用sigmoid
4.6 高级优化方法
4.6.1 Adam 算法 自适应距估计 ——自动调节学习率大小

需要一个默认的学习率,建议可以选一个大点,一个小点的多试
4.7 其他网络层的类型
- dense layer 密集层就是全连接层一种隐藏层,其中的每个节点均与下一个隐藏层中的每个节点相连。
- convolutional layer 卷积层
例子:心电图识别,判断是否有心脏病,二分类
需要考虑的参数:
单个神经元应该观 察的输入窗口有多大?
一层应该有几个神经元?




