Made with ❤️ && for Freedom
最近参加美赛可能要用到神经网络,所以稍微学了一下,姑且把我的学习过程写出来吧。本系列不会讲解深度学习中的数学原理,直接用“习惯用法”代替了研究的数学证明,需要了解其中的数学原理的推荐吴思达的公开课。
我使用 Tensorflow 框架,直接在 Google Colab 上运行,在这里可以直接打开使用:AI Learning.ipynb,但是还是建议自己重新打一遍代码,否则很可能做完之后还是什么都不会。
基本原理
从亚马逊云抄来的原理,算一个检测吧,先得看得懂这些东西再继续,不然很可能完全不知道在做什么。
神经网络的工作原理是什么?
神经网络架构的灵感源自人脑。人脑细胞称为神经元,构成了一个复杂、高度互联的网络,并能互相发送电信号,以帮助人类处理信息。同样,人工神经网络由人工神经元组成,它们共同合作以解决问题。人工神经元是软件模块,称为节点;人工神经网络是软件程序或算法,在其核心,使用计算系统求解数学计算。
简单神经网络架构
基本神经网络的相互连接的人工神经元分为三层:
1. 输入层
来自外部世界的信息通过输入层进入人工神经网络。输入节点对数据进行处理、分析或分类,然后将其继续传递到下一层。
2. 隐藏层
隐藏层从输入层或其他隐藏层获取其输入。人工神经网络可以具有大量的隐藏层。每个隐藏层都会对来自上一层的输出进行分析和进一步处理,然后将其继续传递到下一层。
3. 输出层
输出层提供人工神经网络对所有数据进行处理的最终结果。它可以包含单个或多个节点。例如,如果我们要解决一个二元(是/否)分类问题,则输出层包含一个输出节点,它将提供 1 或 0 的结果。但是,如果我们要解决一个多类分类问题,则输出层可能会由一个以上输出节点组成。
深度神经网络架构
深度神经网络又名深度学习网络,拥有多个隐藏层,包含数百万个链接在一起的人工神经元。名为权重的数字代表节点之间的连接。如果节点之间相互激励,则该权重为正值,如果节点之间相互压制,则该权重为负值。节点的权重值越高,对其他节点的影响力就越大。
从理论上讲,深度神经网络可将任何输入类型映射到任何输出类型。但与其他机器学习方法相比,它们也需要更多大量的训练。它们需要数百万个训练数据示例,而不像较简单的网络那样,可能只需数百或数千个训练数据示例。
个人理解
简单来说神经网络可以看作一个整体的函数,里面可以有数以亿计的参数(参数数量是我们设定的)。通过不断的调整参数来调整结果,使输出达到能继续预测的准确度。
有一点像评价类模型,常规评价类模型的建模相当于人为调整参数,但是使用神经网络就是在利用数学原理自动修改参数。
自然而然,整个建模过程就分为两大部分:训练、预测。训练就是调整参数的过程,而预测是使用这些参数求得结果的过程。当然预测过程也需要求结果,注意区分。
数据准备
计算机只能处理二进制数据,十进制转二进制非常容易,而图片的本质就是每个像素点的 R 、G 、B 十进制数值。这就是计算机显示图像的原理。为了将图像输入 Python 中进行处理,我们得约定一种数据表示方法。
import numpy as np
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from tensorflow.keras.utils import load_img, img_to_array
from PIL import ImageOps
img_path = 'xxx.jpg'
img = load_img(img_path) # 导入图片文件,我预处理了图片,每个图片分辨率均为 28*28
img = ImageOps.grayscale(img) # 转换为灰度图像,将 RGB 图片转化为灰度图片。
x = np.array(img).astype('float32') # 将 28*28 图片数据展平成 784 维向量
x = np.expand_dims(x, axis=0) # 符合数据网络数据的要求
x = x/255.0 # 归一化数据,让所有的数据都在 0~1 这个区间内
现在只要人工写几万张手写图片,并且先人工识别出每张图片的意思,输入 Python 中就可以开始训练了!
是不是很麻烦?这就是神经网络的重点,要有庞大且优质的数据集。这个数据集才是神经网络算法的核心,得到优质的数据集甚至比选择好的模型还重要。
对于手写数字识别来说,已经有整理好的数据集,直接导入就行了。这里我们顺便把依赖导入了吧:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models
from tensorflow.keras.models import load_model
from tensorflow.keras.callbacks import ModelCheckpoint
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
关键的数据导入就是最后两行代码,从库中读取数据。数据分为四个组:
train_images
: 训练集图像的 numpy 数组,形状为 (60000, 28, 28)。train_labels
: 训练集标签的 numpy 数组,形状为 (60000,),每个标签是一个整数,表示对应图像的数字。test_images
: 测试集图像的 numpy 数组,形状为 (10000, 28, 28)。test_labels
: 测试集标签的 numpy 数组,形状为 (10000,),每个标签是一个整数,表示对应图像的数字。
搭建神经网络
先看代码
model = models.Sequential([
layers.Flatten(input_shape=(28, 28)), # 输入层,将 28x28 的图片矩阵展平成 784 维向量
layers.Dense(128, activation='relu'), # 隐藏层,128 个神经元,使用 ReLU 激活函数
layers.Dropout(0.2),
layers.Dense(128, activation='relu'), # 隐藏层,128 个神经元,使用 ReLU 激活函数
layers.Dropout(0.2),
layers.Dense(10, activation='softmax') # 输出层,10 个神经元,使用 Softmax 激活函数,用于多分类问题
])
很容易理解,models.Sequential
中的一个参数就是一层,填入的数字是该层的节点数,activation
则表示该层使用什么激活函数,是上一层到这一层的映射关系。
激活函数在这里不多做介绍,以后有机会再写文章详细说。
Dropout
层的主要参数是丢弃率(dropout rate),它是一个 0 到 1 之间的浮点数,表示每次训练迭代时随机丢弃的神经元比例。例如,Dropout(0.2)
表示每次训练迭代时随机丢弃 20% 的神经元输出,使得网络不会过于依赖于某些特定神经元。这可以提高模型的泛化能力,从而在测试集上获得更好的性能。
层数、节点数和丢弃率都需要不断尝试。对于手写识别这种简单的分类问题一两层就够了,层数过多反而容易出现过拟合的问题。
模型训练与验证
- 编译模型:指定优化器、损失函数、训练目标
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
- 训练模型:用 x_train 和 y_train 数据训练模型,epochs 用于指定训练的次数
model.fit(x_train, y_train, epochs=10)
- 验证模型:用 x_test 和 y_test 验证模型的效果。作为操作者,我们可以根据这个结果来调整模型。
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc}")
手写数字识别是个很简单的任务,不需要迭代几百次,用 Colab 几秒钟就能完成,所以也无需保存模型、重新训练,但是如果模型大了,保存、读取就显得越发重要:
保存模型:codemodel.save("mnist_model.h5")
加载模型:
from tensorflow.keras.models import load_model
loaded_model = load_model("mnist_model.h5")
使用加载的模型进行预测:
prediction = loaded_model.predict(input_image)
predicted_label = np.argmax(prediction)
print(f"Predicted label: {predicted_label}")
继续训练模型:
from tensorflow.keras.models import load_model
loaded_model = load_model("mnist_model.h5")
# 编译模型(可选):如果您在保存模型之前已经编译过模型,那么在加载模型时,编译信息也会被保留。但是,如果您希望改变优化器、损失函数或指标,您可以在加载模型后重新编译模型。
loaded_model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 继续训练模型:使用fit()方法继续训练模型。在这个例子中,我用MNIST数据集的训练数据和2个额外的训练周期(epochs)。
loaded_model.fit(x_train, y_train, epochs=2)
# 评估模型性能
test_loss, test_acc = loaded_model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc}")
# 保存更新后的模型
loaded_model.save("mnist_model_updated.h5")
如果想要在中途保存权重 (weigh),参考:
# 计算每五个周期的批次数
save_freq_batches = len(x_train) // 5
# 创建 ModelCheckpoint 回调以在训练期间保存权重检查点
checkpoint_path = "checkpoints/weights_cp-{epoch:04d}.ckpt"
checkpoint_callback = ModelCheckpoint(filepath=checkpoint_path,
save_weights_only=True,
save_freq=save_freq_batches, # 每五个周期保存一次权重
verbose=1)
# 使用 fit() 方法进行训练
loaded_model.fit(x_train, y_train, epochs=100, callbacks=[checkpoint_callback])
loaded_model.save("mnist_model_updated.h5")
使用模型进行预测
import numpy as np
from PIL import ImageOps
from tensorflow.keras.preprocessing.image import load_img, img_to_array
img_path = '3.jpg'
img = load_img(img_path, color_mode='grayscale', target_size=(28, 28))
img_array = img_to_array(img)
# img_array = 255 - img_array # 如果输入图像的背景是白色,这会将其反转为黑色背景
img_array = img_array.astype('float32')
img_array = np.expand_dims(img_array, axis=0)
img_array /= 255
pred = model.predict(img_array)
class_idx = np.argmax(pred[0])
print(pred[0])
print(class_idx)
在使用自己的图片之前要进行数据处理,反而是预测的代码非常简单,model.predict(x)
。
完整代码
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.models import load_model
from tensorflow.keras.callbacks import ModelCheckpoint
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 = models.Sequential([
layers.Flatten(input_shape=(28, 28)), # 输入层,将 28x28 的图片矩阵展平成 784 维向量
layers.Dropout(0.1),
layers.Dense(64, activation='relu'), # 隐藏层,64 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(128, activation='relu'), # 隐藏层,128 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(64, activation='relu'), # 隐藏层,64 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(256, activation='relu'), # 隐藏层,256 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(64, activation='relu'), # 隐藏层,64 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(128, activation='relu'), # 隐藏层,128 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(64, activation='relu'), # 隐藏层,64 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(32, activation='relu'), # 隐藏层,32 个神经元,使用 ReLU 激活函数
layers.Dropout(0.1),
layers.Dense(10, activation='softmax') # 输出层,10 个神经元,使用 Softmax 激活函数,用于多分类问题
])
# 创建 ModelCheckpoint 回调
checkpoint_path = "checkpoints/cp-{epoch:04d}.ckpt"
checkpoint_callback = ModelCheckpoint(filepath=checkpoint_path,
save_weights_only=True,
save_freq='epoch',
verbose=1)
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# model.fit(x_train, y_train, epochs=1, callbacks=[checkpoint_callback])
model.fit(x_train, y_train, epochs=20)
test_loss, test_acc = model.evaluate(x_test, y_test)
model.save("hello1.h5")
print(f"Test accuracy: {test_acc}")
# 预测
import numpy as np
from PIL import ImageOps
from tensorflow.keras.preprocessing.image import load_img, img_to_array
img_path = '3.jpg'
img = load_img(img_path, color_mode='grayscale', target_size=(28, 28))
img_array = img_to_array(img)
# img_array = 255 - img_array # 如果输入图像的背景是白色,这会将其反转为黑色背景
img_array = img_array.astype('float32')
img_array = np.expand_dims(img_array, axis=0)
img_array = img_array / 255.0
pred = model.predict(img_array)
class_idx = np.argmax(pred[0])
print(pred[0])
print(class_idx)
自己调一调参数吧!
LemonPig2023-04-08 19:33
请收下我的敬佩!我对AI向来有至高的兴趣,但是从未给自己机会真正实践。而今天有幸在这个满是宝藏的blog上看到了这篇实践记录,自己感到不胜惭愧与羡慕。以后我也想从事人工智能行业,看来我需要学习的还有很多!
admin2023-04-08 19:42
我是门外汉——大佬不要被我误导了——