机器学习周报(9.23-9.29)

目录

摘要

Abstract

1 训练神经网络(Neural Network Training)

1.1 TensorFlow实现(TensorFlow Implementation)

1.2 训练细节(Training Details)

2 激活函数(Activation Functions)

2.1 Sigmoid的替代品(Alternatives to The Sigmoid Activation)

2.2 激活函数的选择(Choosing Activation Functions)

2.3 激活函数的重要性

3 实战:TensorFlow实现手写识别数字0和1

3.1 加载数据集

3.2 构建模型与训练

3.3 结果可视化与打印测试数据集准确度信息

3.4 运行结果

4 多类分类(Multiclass Classification)

4.1 多类(Multiclass)

4.2 Softmax

4.3 神经网络的Softmax输出

4.4 Softmax的改进实现

5 实战:TensorFlow实现手写识别数字0~9

5.1 加载数据集

5.2 构建模型与训练

5.3 结果可视化与打印测试数据集结果信息

5.4 运行结果

总结


摘要

本周继续学习吴恩达机器学习的神经网络部分。了解了TensorFlow训练神经网络的细节,以及如何选择激活函数,并用代码简单实现了手写识别数字0和1。然后了解了什么是多类分类和Softmax回归函数,并通过改进Softmax加强计算精确度,最后用代码实现了手写识别数字0到9。

Abstract

This week, I continued studying the neural network section of Andrew Ng's machine learning course. I learned the details of training neural networks with TensorFlow, including how to choose activation functions. I implemented a simple code for recognizing handwritten digits 0 and 1. Then, I explored what multi-class classification is and the Softmax regression function. By improving the Softmax implementation, I enhanced computational accuracy, and finally, I used code to recognize handwritten digits from 0 to 9.

1 训练神经网络(Neural Network Training)

1.1 TensorFlow实现(TensorFlow Implementation)

继续手写数字识别的运行示例,识别图像是0还是1,依旧使用之前的神经网络架构:

在TensorFlow中可以用来训练这个网络的代码如下:

import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
model = Sequential([
    Dense(units = 25, activation = 'sigmoid'),
    Dense(units = 15, activation = 'sigmoid'),
    Dense(units = 1, activation = 'sigmoid')
)]

用TensorFlow将三层神经网络依次串在一起,第一层隐藏层有25个单位和S形激活函数,接下来是第二层,最后是输出层。

第二步是让TensorFlow编译模型,关键在于指定要使用的损失函数,在这里使用到稀疏绝对交叉熵(Sparse Categorical Cross-entropy):

from tensorflow.keras.losses import BinaryCrossentropy
model.compile(loss = BinaryCrossentropy())

在指定了损失函数后第三步是调用fit函数,它告诉TensorFlow适配第一步中的模型和使用第二步中的损失代价函数到数据集中:

model.fit(X, Y, epochs = 100)

以上,在TensorFlow中训练神经网络需要三步:

  1. 构造模型;
  2. 定义代价函数,进行编译;
  3. 拟合函数。

1.2 训练细节(Training Details)

回想在学习逻辑回归时,是如何训练一个逻辑回归模型的:

  1. 建立一个逻辑回归模型,给定第一个过程中的输入特征x 和参数w,b ,再应用S形函数g ;
  2. 训练逻辑回归模型,指定损失函数L 和代价函数J ;
  3. 使用算法,特别是梯度下降算法最小化J(\vec{w},b) 。

同样,在神经网络中 :

  1. 指定如何计算输出给定的输入特征x 和参数w,b ;
  2. 编译模型,告诉TensorFlow想用什么损失函数,也就是二元交叉熵(Binary Cross-entropy)损失函数;
  3. 调用一个作为神经网络参数的函数,试图最小化成本。

放大来看,第一步指定如何计算输出给定的输入特征x 和参数w,b ,此代码段指定神经网络的整个体系结构,它告诉我们在第一个隐藏层中有25个隐藏单元,第二个隐藏层有15个,然后最后有一个输出单元。使用S形函数激活值,所以基于这个代码段我们也知道第一层、第二层或第三层的参数是什么。所以这个代码段指定了神经网络的整个架构。

常见的损失函数是二元交叉熵损失函数、平方差(Mean Squared Error)损失函数:

在TensorFlow里使用的是Fit函数,能够实现反向传播:

2 激活函数(Activation Functions)

2.1 Sigmoid的替代品(Alternatives to The Sigmoid Activation)

使用上周需求预测的例子,可能不止单单0或1两种选择,可能存在0到无穷大的范围,则此时S形函数不能适用,我们可以用不同的激活函数。在神经网络中,一个非常常见的激活函数是如下这个ReLU(Rectified Linear Unit,矫正线性)函数g(z)=max(0,z),当z<0 时,g(z)=0 ;当z\geqslant 0 时,g(z)=z :

以下是最常见的三个激活函数,线性激活函数、Sigmoid激活函数和ReLU激活函数:

2.2 激活函数的选择(Choosing Activation Functions)

在神经网络的输出层中:

  • 当遇到二元分类问题时,Sigmoid激活函数无疑是最好的选择,用来分辨0或1;
  • 如何试图预测明天的股价与今天的股价相比会有什么变化,这类回归问题可以使用线性激活函数;
  • 如果是非负的回归问题,则ReLU激活函数最适用。

在神经网络的隐藏层中,由于ReLU激活函数比Sigmoid激活函数计算更快和梯度下降更快,所以ReLU激活函数是最常见的选择:

在TensorFlow代码实现如下:

from tf.keras.layers import Dense
model = sequential([
    Dense(units = 25, activation = 'relu'),
    Dense(units = 15, activation = 'relu'),
    Dense(units = 1, activation = 'sigmoid')
])

每年都会有新的激活函数提出,例如Tenh激活函数、Leaky ReLU激活函数、Swish激活函数等。

2.3 激活函数的重要性

如果对需求预测的神经网络均使用线性激活函数,这个神经网络将变得与仅仅是线性回归没有什么不同,从而无法适用其他更复杂的模型。

如果像下图这样化成两层去线性回归,还不如直接用一个线性回归:

每层中都用线性回归,最后用sigmoid激活函数的话,结果出来跟逻辑回归没区别:

所以,不要在神经网络的隐藏层中使用线性激活函数,而应该使用ReLU激活函数。

3 实战:TensorFlow实现手写识别数字0和1

3.1 加载数据集

# 加载数据集
def load_data():
    X = np.load("手写识别01/X.npy")
    y = np.load("手写识别01/y.npy")
    X = X[0:1000]
    y = y[0:1000]
    return X, y

# 加载数据集,查看数据集大小
X, y = load_data()
print('The shape of X is: ' + str(X.shape))
print('The shape of y is: ' + str(y.shape))
# 划分10%作为测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=23)

构造load_data函数,用于加载npy文件中的数据集。

train_test_split函数用于将数据分割成训练集和测试集。

可以看到有1000个数据集,每个输入是20*20=400大小的图片:

3.2 构建模型与训练

通过构造三层模型进行分类,隐藏层使用ReLU激活函数,输出层使用Sigmoid激活函数。

然后告诉模型需要什么样的损失函数,这里使用的损失函数是二元交叉熵(BinaryCrossentropy)损失函数。

最后开始调用fit函数进行循环训练。

# 构建模型,三层模型进行分类,第一层输入25个神经元...
model = Sequential([
        tf.keras.Input(shape=(400,)),    #指定输入大小

        Dense(25, activation='relu'),
        Dense(15, activation='relu'),
        Dense(1, activation='sigmoid')
])

# 模型设定,因为是分类,使用BinaryCrossentropy损失函数
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy()
)

# 开始训练,训练循环10次
model.fit(
    X_train,y_train,
    epochs=10
)

 Sequential模型是Keras中的线性堆栈模型,允许简单地堆叠多个网络层。

Dense层是神经网络中的全连接层,每个输入节点与输出节点都是连接的。

可以看到每一次训练所用时长和损失函数的值:

3.3 结果可视化与打印测试数据集准确度信息

原始的输入的数据集是400 * 1000的数组,共包含1000个手写数字的数据,其中400为20*20像素的图片,因此对每个400的数组进行reshape((20, 20))可以得到原始的图片进而绘图。

# 绘制测试集的预测结果,绘制25个
fig, axes = plt.subplots(5, 5, figsize=(5, 5))  # 画窗、坐标轴
fig.tight_layout(pad=0.1, rect=[0, 0.03, 1, 0.88])  # [left, bottom, right, top]
for i, ax in enumerate(axes.flat):
    # 随机选取索引
    random_index = np.random.randint(X_test.shape[0])

    # 选择与随机索引对应的行并对图像进行重构
    X_random_reshaped = X_test[random_index].reshape((20, 20)).T

    # 显示图像
    ax.imshow(X_random_reshaped, cmap='gray')

    # 使用神经网络进行预测
    prediction = model.predict(X_test[random_index].reshape(1, 400))
    if prediction >= 0.5:
        yhat = 1
    else:
        yhat = 0

    # 在图像上方显示标签
    ax.set_title(f"{y_test[random_index, 0]},{yhat}")
    ax.set_axis_off()
fig.suptitle("真实标签, 预测的标签", fontsize=16)
plt.show()

# 给出预测的测试集误差
y_pred=model.predict(X_test)
print("测试数据集准确率为:", accuracy_score(y_test, np.round(y_pred)))

pyplot是Matplotlib中的一个模块,用于绘制各种图形和图像。

能看到神经网络对每个图片的预测时长:

3.4 运行结果

按照最初的划分,数据集包含1000个数据,划分10%为测试集,也就是100个数据。结果可视化随机选择其中的25个数据绘图,每个图像的上方标明了其真实标签和预测的结果:

最后可以看到测试数据集的准确率还是很高的:

4 多类分类(Multiclass Classification)

4.1 多类(Multiclass)

多类分类指的是分类问题,在这些问题中,可以有不止两个可能的输出标签。

对于手写识别来说,是区分0-9这十个数字。或者是试图分类病人是否可能患有几种不同的疾病中的一种或多种。

在多类问题中要算的是P(y=1|\vec{x}) 或P(y=2|\vec{x}) 或P(y=3|\vec{x}) 或P(y=4|\vec{x}) :

需要学习一个决策边界,它可能看起来像列表,将空间分为四个类别。

4.2 Softmax

Softmax算法是逻辑回归的推广,它是一种针对多类分类上下文的二进制分类算法。

在逻辑回归中只有两种可能的输出值1或0,当P(y=1|\vec{x})=0.71 时,可以得到P(y=0|\vec{x})=1-0.71=0.29 。同样,当这个方法推广到SoftMax回归中也适用,假设存在四种可能的值(y=1,2,3,4),若P(y=1|\vec{x})=0.30 、P(y=2|\vec{x})=0.20 、P(y=3|\vec{x})=0.15 ,则P(y=4|\vec{x})=1-0.30-0.20-0.15=0.35 。

由此可得,在一般情况下的Softmax回归有N 种可能值(y=1,2,3,...,N),有:

z_{j}=\vec{w}_{j}\cdot \vec{x}+b_{j}\; \; \; j=1,2,3,...,N

a_{j}=\frac{e^{z_{j}}}{\sum^{N}_{k=1} e^{z_{k}}}=P(y=j|\vec{x})

且:a_{1}+a_{2}+a_{3}+...+a_{N}=1 。

N=2 时,Softmax回归最终计算的结果与逻辑回归的结果基本相同,只是参数可能会有点不同,但它最终可以简化为逻辑回归模型。所以Softmax回归模型是逻辑回归的推广。

关于如何指定Softmax回归的成本函数,依旧是参照逻辑回归中的成本函数,则可以得到:

loss(a_{1},...,a_{N},y)=\left\{\begin{matrix} -log\, a_{1}\; \; \; \; if\; y=1 \\ -log\, a_{2}\; \; \; \; if\; y=2 \\\cdot \cdot \cdot \\ -log\, a_{N}\; \; \; \; if\; y=N \end{matrix}\right. 

loss=-log\, a_{j}\;\;\;if\;y=j 。

在这个损失函数中,每个训练示例只能具有一个值,即计算出的-log\, a_{j} 仅对应一个a_{j} 。

4.3 神经网络的Softmax输出

如果想要对0-9这十个数字进行手写瘦瘪,则需要十个输出类,然后可以沿用在之前的手写识别0和1时用到的三层神经网络,第一层隐藏层依旧是25个神经元,第二层隐藏层是15个神经元,并且第一第二层使用之前的ReLU激活函数,第三层输出层则为10个激活值组成的向量\vec{a}_{3} 。这十个输出类的表达式为:

z^{[3]}_{1}=\vec{w}^{[3]}_{1}\cdot \vec{a}^{[2]}+b^{[3]}_{1}\; \; \; \; \; \; \; \; a^{[3]}_{1}=\frac{e^{z^{[3]}_{1}}}{e^{z^{[3]}_{1}}+...+e^{z^{[3]}_{10}}}=P(y=1|\vec{x})

\cdot \cdot \cdot

z^{[3]}_{10}=\vec{w}^{[3]}_{10}\cdot \vec{a}^{[2]}+b^{[3]}_{10}\; \; \; \; \; \; \; \; a^{[3]}_{10}=\frac{e^{z^{[3]}_{10}}}{e^{z^{[3]}_{1}}+...+e^{z^{[3]}_{10}}}=P(y=10|\vec{x})

z^{[3]}_{1} 表明这是与这个神经网络第三层的第一个单元相关的参数。

有了上面这个表达式,Softmax就能输出这十个可能的输出标签中的任何一个了。

在之前的逻辑回归中的三种激活函数(Sigmoid、RuLU、Linear),一个z 决定一个a 。但是在Softmax激活函数中,每一个激活值a 都依赖于所有的z (z_{1} 到z_{10} ),这与前面的几个激活函数都有所不同。

在tensorflow中代码表示如下:

import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
model = Sequential([
        Dense(25, activation='relu'),
        Dense(15, activation='relu'),
        Dense(10, activation='softmax')
])

from tensorflow.keras.losses import SparseCategoricalCrossentropy
model.compile(
    loss = SparseCategoricalCrossentropy()
)

model.fit(
    X,Y,
    epochs=100
)

第三层模型为10个神经元,激活函数为Softmax。

损失函数为稀疏范畴交叉熵函数(SparseCategoricalCrossentropy),稀疏指的是y 只能接受0-9这十个值中的一个,所以每个图像要么是0要么是1要么是9,不会看到同时是数字2和数字7的图片。

然后就是循环进行训练模型。

4.4 Softmax的改进实现

计算机只有在有限的内存来存储每个数字,因此会存在一些计算误差,例如下面这个例子:

为了在TensorFlow中进行精确的计算,则需要减少一定的误差。在逻辑回归中,如果要计算给定实例的损失函数loss=-ylog(a)-(1-y)log(1-a) ,由于用到了中间项a 则会产生如上述例子带来的误差,因此将表达式展开为loss=-ylog(\frac{1}{1+e^{-z}})-(1-y)log(1-\frac{1}{1+e^{-z}}) ,从而能够用一个更精确的数值方法来计算这个损失函数。

然后可以将代码改写为,在第三层输出层使用线性激活函数,将\frac{1}{1+e^{-z}} 置于这个交叉熵损失函数的说明中。

由此我们可以把这个想法应用到Softmax回归中,同样在第三层输出层使用线性激活函数只计算z_{1} 到z_{10} ,然后将整个损失的计算都包含在这里的损失函数中,从而使计算更加精确了,如下:

# 构建模型
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
model = Sequential([
    Dense(100, activation='relu'),
    Dense(75, activation='relu'),
    Dense(10, activation='linear'),
])


# 配置模型的训练参数
from tensorflow.keras.losses import SparseCategoricalCrossentropy
model.compile(
    loss=SparseCategoricalCrossentropy(from_logits=True)
)


# 训练模型
model.fit(
    X, Y, 
    epochs=100
)

# 模型预测
logit = model(X)
f_x = tf.nn.sigmoid(logit)

5 实战:TensorFlow实现手写识别数字0~9

5.1 加载数据集

# 加载数据集
def load_data():
    X = np.load("手写识别09/X.npy")
    y = np.load("手写识别09/y.npy")
    return X, y
X, y = load_data()

# 查看数据集大小
print ('The shape of X is: ' + str(X.shape))
print ('The shape of y is: ' + str(y.shape))
#划分10%作为测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=23)

 构造load_data函数,用于加载npy文件中的数据集。

train_test_split函数用于将数据分割成训练集和测试集。

可以看到有5000个数据集,每个输入是20*20=400大小的图片:

5.2 构建模型与训练

通过构造三层模型进行分类,隐藏层使用ReLU激活函数,为确保计算精准性,输出层使用线性激活函数。

然后告诉模型需要什么样的损失函数,这里使用的损失函数是稀疏分类交叉熵(Sparse Categorical Crossentropy)损失函数。

最后开始调用fit函数进行循环训练。

# 构建模型
tf.random.set_seed(23)  # 设置随机种子以确保每次运行的结果是一致的
model = Sequential([
        tf.keras.Input(shape=(400,)),  # 输入层,输入数据的形状是400维
        Dense(25, activation='relu'),  # 全连接层,25个神经元,使用ReLU激活函数
        Dense(15, activation='relu'),  # 全连接层,15个神经元,使用ReLU激活函数
        Dense(10, activation='linear'),  # 输出层,10个神经元,使用线性激活函数
])

# 配置模型的训练参数
model.compile(
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
    # 使用稀疏分类交叉熵作为损失函数,且输出是logits(即未经过softmax的原始输出)
)

# 训练模型
model.fit(
    X_train, y_train,  # 使用X_train作为输入数据,y_train作为目标数据
    epochs=100  # 训练100轮
)

 训练过程如下:

5.3 结果可视化与打印测试数据集结果信息

原始的输入的数据集是400 * 5000的数组,共包含5000个手写数字的数据,其中400为20*20像素的图片,因此对每个400的数组进行reshape((20, 20))可以得到原始的图片进而绘图。

# 绘制测试集的预测结果,绘制15*10=150个
fig, axes = plt.subplots(15, 10, figsize=(15, 10))
fig.tight_layout(pad=0.13, rect=[0, 0.03, 1, 0.91])  # [left, bottom, right, top]
for i, ax in enumerate(axes.flat):
    # 随机选取索引
    random_index = np.random.randint(X_test.shape[0])
    
    # 选择与随机索引对应的行并对图像进行重构
    X_random_reshaped = X_test[random_index].reshape((20, 20)).T

    # 显示图像
    ax.imshow(X_random_reshaped, cmap='gray')
    
    # 使用神经网络进行预测
    prediction = model.predict(X_test[random_index].reshape(1, 400))
    prediction_p = tf.nn.softmax(prediction)
    yhat = np.argmax(prediction_p)
    
    # 在图像上方显示标签
    if y_test[random_index, 0] == yhat:
        ax.set_title(f"{y_test[random_index, 0]},{yhat}", fontsize=10)
        ax.set_axis_off()
    else:
        ax.set_title(f"{y_test[random_index, 0]},{yhat}", fontsize=10, color='red')
        ax.set_axis_off()

fig.suptitle("Label, yhat", fontsize=14)
plt.show()

# 给出预测的测试集误差
def evaluation(y_test, y_predict):
    accuracy=classification_report(y_test, y_predict,output_dict=True)['accuracy']
    s=classification_report(y_test, y_predict,output_dict=True)['weighted avg']
    precision=s['precision']
    recall=s['recall']
    f1_score=s['f1-score']
    #kappa=cohen_kappa_score(y_test, y_predict)
    return accuracy,precision,recall,f1_score #, kappa

y_pred=model.predict(X_test)
prediction_p = tf.nn.softmax(y_pred)
yhat = np.argmax(prediction_p, axis=1)
accuracy,precision,recall,f1_score=evaluation(y_test,yhat)

print("测试数据集准确率为:", accuracy)
print("测试数据集精确率为:", precision)
print("测试数据集召回率为:", recall)
print("测试数据集F1_score为:", f1_score)

能看到神经网络对每个图片的预测时长:

5.4 运行结果

按照最初的划分,数据集包含5000个数据,划分10%为测试集,也就是500个数据。结果可视化随机选择其中的150个数据绘图,每个图像的上方标明了其真实标签(Label)和预测的结果(yhat):

最后可以看到测试数据集的准确率还是很高的:

总结

通过本周的学习,了解了TensorFlow训练神经网络的细节,首先需要构建模型:使用Keras.Sequential定义神经网络模型、层的选择、选择激活函数,然后编译模型以指定损失函数和优化器,最后设置训练参数使用训练数据训练模型。

然后知道了该如何选择激活函数(Linear、Sigmoid、ReLU),需要考虑到任务类型:分类、回归等不同任务可能需要不同的激活函数,网络结构深度:深层网络常用ReLU系列以避免梯度消失,训练速度与稳定性:某些激活函数可能导致训练不稳定。

还知道了什么是多类分类。多类分类是机器学习中的一种任务,旨在将输入数据分到多个类别中。与二分类问题不同,多类分类涉及三个或更多的类别。为确保计算精确度还要对Softmax回归进行一定代码改进。

最后通过实战TensorFolw实现手写数字识别0和1以及识别0到9,加强了对TensorFlow和神经网络训练过程的理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值