【深度学习】Keras人工神经网络简介(理论+实战)
感知器是最简单的ANN架构之一,它基于阈值逻辑单元(TLU)。信号仅沿一个方向(从输入到输出)流动,以下架构是前馈神经网络(FNN)的示例。创建一个隔离的Python环境:如果创建了virtualenv,则需要注册到Jupyter,并给它一个名字;创建一个Sequential模型(仅由顺序连接的单层堆栈组成,称为顺序API)
从生物神经元到人工神经元(理论知识)
感知器
-
感知器是最简单的ANN架构之一,它基于阈值逻辑单元(TLU)。输入输出是数字(而不是二进制开关值),并且每个输入连接都与权重相关联。TLU计算其输入的加权和( z = w 1 x 1 + w 2 x 2 + . . . + w n x n = x ⊤ w z=w_1x_1+w_2x_2+...+w_nx_n=x^\top w z=w1x1+w2x2+...+wnxn=x⊤w),然后将阶跃函数应用于该和并输出结果:( h w ( x ) = s t e p ( z ) h_w(x)=step(z) hw(x)=step(z)),其中, z = x ⊤ w z=x^\top w z=x⊤w。
-
感知机中使用的常见阶跃函数(假设阈值=0)
-
单个TLU可用于简单的线性二进制分类。
-
感知器仅由单层TLU组成,每个TLU连接到所有的输入。当一层中的所有神经元都连接到上一层中的每个神经元(即其输入神经元),该层称为全连接层或密集层。
-
感知器的输入被送到称为输入神经元的特殊直通神经元:它们输出被送入的任何输入。所有输入神经元形成输入层。此外,通常会添加一个额外的偏置特征( x 0 = 1 x_0=1 x0=1):通常使用一种称为偏置神经元的特殊类型的神经元来表示该特征,该神经元始终输出1。下图的感知器可以将实例同时分为三个不同的二进制类,这使其成为多输出分类器。
-
计算全连接层的输出
h W , b = ϕ ( X W + b ) h_{W,b}=\phi (XW+b) hW,b=ϕ(XW+b)
X代表输入特征的矩阵。每个实例一行,每个特征一列。
权重矩阵W包含除偏置神经元外的所有连接权重。在该层中,每个输入神经元一行,每个人工神经元一列。
偏置向量b包含偏置神经元和人工神经元之间的所有连接权重。每个人工神经元有一个偏置项。
函数 ϕ \phi ϕ称为激活函数:当人工神经元是TLU时,它是阶跃函数。 -
感知器如何训练?Hebb规则:两个神经元同时触发时,它们之间的连接权重会增加。具体来说,感知器一次被送入一个训练实例,并且针对每个实例进行预测。对于产生错误预测的每个输出神经元,它会增强来自输入的连接权重,这些权重将有助于正确的预测。感知器学习规则(权重更新):
w i , j ( 下一步 ) = w i , j + η ( y j − y j ^ ) x i w_{i,j}^{(下一步)}=w_{i,j}+\eta (y_j-\hat{y_j})x_i wi,j(下一步)=wi,j+η(yj−yj^)xi
w i , j w_{i,j} wi,j是第 i i i个输入神经元和第 j j j个输出神经元之间的连接权重。
x i x_i xi是当前训练实例的第 i i i个输入值。
y j ^ \hat{y_j} yj^是当前训练实例的第 j j j个输出神经元的输出。
y j y_j yj是当前训练实例的第 j j j个输出神经元的目标输出。
η \eta η是学习率。 -
每个输出神经元的决策边界都是线性的,因此感知器无法学习复杂的模式(就像逻辑回归分类器一样)。
-
Scikit-Learn提供了一个Perceptron类,该类实现了单个TLU网络。
from sklearn.datasets import load_iris
from sklearn.linear_model import Perceptron
iris = load_iris()
X = iris.data[:, (2, 3)] # petal length, petal width
y = (iris.target == 0).astype(int) # if iris.target == 0: 1, else 0.
per_clf = Perceptron()
per_clf.fit(X, y)
y_pred = per_clf.predict([[2, 0.5]])
print(y_pred)
- 感知器无法解决一些问题(例如,异或(XOR)分类问题),可通过堆叠多个感知器来消除感知器的某些局限性,所得的ANN称为多层感知器(MLP)。
多层感知器和反向传播
- 信号仅沿一个方向(从输入到输出)流动,以下架构是前馈神经网络(FNN)的示例。
- 当一个ANN包含一个深层的隐藏层时,称为深度神经网络(DNN)。
- 反向传播算法能够针对每个模型参数计算网络误差的梯度。换句话说,它可以找出应如何调整每个连接权重和每个偏置项以减少误差。一旦获得了这些梯度,它便会执行常规的梯度下降步骤,然后重复整个过程,直到网络收敛到解。
- 该算法:对于每个训练实例,反向传播算法首先进行预测(正向传递)并测量误差,然后反向经过每个层以测量来自每个连接的误差贡献(反向传递),最后调整连接权重以减少错误(梯度下降步骤)。
- 随机初始化所有隐藏层的连接权重很重要。
回归MLP
- 在构建用于回归的MLP时,若不对输出神经元使用任何激活函数,它们可以输出任何范围的值;若要保证输出始终为正,可以使用ReLU激活函数,也可以使用softplus激活函数,它是ReLU是平滑变体:
s
o
f
t
p
l
u
s
(
z
)
=
l
o
g
(
1
+
e
x
p
(
z
)
)
softplus(z)=log(1+exp(z))
softplus(z)=log(1+exp(z)),当z为负时,它接近于0,当z为正时,它接近于z;若要保证预测值在给定的值范围内,则可以使用逻辑函数或双曲正切,然后将标签缩放到给定的值范围内:逻辑函数的范围为0到1,双曲正切为-1到1。
分类MLP
使用Keras实现MLP(实战)
安装 Jupyter 以及 TensorFlow 2
- 如果希望在一个隔离的环境里工作(这样可以在库版本不冲突的情况下处理不同的项目),可以通过运行以下pip命令来安装 virtualenv:
python -m pip install virtualenv
输入以下命令创建一个隔离的Python环境:
cd E:\大四\大四上\NLP\ML
python -m virtualenv my_env
现在开始,每当想要激活这个环境,只需要打开终端并输入:
cd E:\大四\大四上\NLP\ML
.\my_env\Scripts\activate
要停用这个环境,用deactivate命令。当这个环境处于激活状态时,使用pip安装的任何软件包都将被安装在这个隔离的环境中,Python只拥有这些包的访问权限。
现在可以使用简单的pip命令安装必必需的模块及其依赖项了:
pip install jupyter matplotlib numpy scipy scikit-learn
- 如果创建了virtualenv,则需要注册到Jupyter,并给它一个名字:
python -m ipykernel install --user --name=python
现在可以输入以下命令,来启动Jupyter:
jupyter notebook
- 一个Jupyter服务器正在终端中运行,监听端口为8888,可以打开浏览器,输入 http://localhost:8888/ 访问该服务器(通常服务器启动时会自动运行)。
单击New按钮,选择适当的Python版本,创建一个Python notebook,它会在你的工作区间创建一个名为 Untitled.ipynb 的新 notebook 文件,然后启动 Jupyter Python 内核来运行这个文件,最后在浏览器新标签里打开这个笔记本。
- 一个notebook内包含一个单元格列表,每个单元格可以是可执行代码或格式化的文本。现在,这个notebook只有一个空的代码单元,标记为“In[1]:”。当单击执行按钮后,当前单元格会被送到 这个notebook 的 Python 内核,运行并返回输出结果。
- 安装好 Jupyter 和 Scikit-Learn 后,使用pip 安装 TensorFlow 2.
pip install tensorflow
要测试安装,则需要打开Python 或 Jupyter notebook,然后导入 TensorFlow 和 tf.keras 并打印其版本。
使用顺序API构建图像分类器
- 首先,我们需要加载数据集。Fashion MNIST 有70000张灰度图像,每幅 28 × 28 28 \times 28 28×28像素,有10个类。
使用Keras加载数据集
- 当使用Keras而不是Scikit-Learn来加载MNIST或Fashion MNIST时,一个重要的区别是每个图像都表示为 28 × 28 28 \times 28 28×28阵列,而不是尺寸为784的一维阵列。此外,像素强度表示为整数(从0到255)而不是浮点数(从0.0到255.0).
import tensorflow as tf
from tensorflow import keras
fashion_mnist = keras.datasets.fashion_mnist
(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()
- 看一下训练集的形状和数据类型:
- 现在创建一个验证集。另外,由于要使用梯度下降训练神经网络,因此需要比例缩放特征。将像素强度除以 255.0,将像素强度降低到0~1范围内:
X_valid, X_train = X_train_full[:5000] / 255.0, X_train_full[5000:] / 255.0
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
- 我们需要一个类名列表来知道我们要处理的内容:
使用顺序API创建模型
- 现在建立神经网络。这是具有两个隐藏层的分类MLP:
model = keras.models.Sequential()
# 创建一个Sequential模型(仅由顺序连接的单层堆栈组成,称为顺序API)
model.add(keras.layers.Flatten(input_shape=[28, 28]))
# 第一层是Flatten层,将每个输入图像转换成一维度组
model.add(keras.layers.Dense(300, activation="relu"))
# 具有300个神经元的Dense隐藏层,使用ReLU激活函数
model.add(keras.layers.Dense(100, activation="relu"))
# 具有100个神经元的Dense隐藏层,使用ReLU激活函数
model.add(keras.layers.Dense(10, activation="softmax"))
# 包含10个神经元的Dense输出层,使用softmax激活函数
- 也可以在创建顺序模型时传递一个层列表:
model_0 = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dense(300, activation="relu"),
keras.layers.Dense(100, activation="relu"),
keras.layers.Dense(10, activation="softmax")
])
- 模型的summary()方法显示模型的所有层,包括每个层的名称(除非在创建层时进行设置,否则会自动生成)
- 获取模型的层列表:
- 可以使用 get_weights() 和 set_weights() 方法访问层的所有参数。对于密集层,这包括连接权重和偏置项:
- 注意,密集层随机初始化了连接权重,并且偏置被初始化为零。如果要使用其他初始化方法,可以在创建层时设置kernel_initializer(内核是连接权重矩阵的另一个名称)或bias_initializer。
编译模型
- 创建模型后,你必须调用compile() 方法来指定损失函数和要使用的优化器。也可以选择指定在训练和评估期间要计算的其他指标:
model.compile(loss="sparse_categorical_crossentropy",
optimizer="sgd",
metrics=["accuracy"])
- 首先,我们使用“sparse_categorical_crossentropy"损失,因为我们具有稀疏标签(即对于每个实例,只有一个目标类索引,在这种情况下为0到9),并且这些类是互斥的。相反,如果每个实例的每个类都有一个目标概率(例如独热向量,[0,0,0,1,0,0,0,0,0,0]代表类3),则我们需要使用”categorical_crossentropy"损失。如果执行二进制分类(带有一个或多个二进制标签),则在输出层中使用“sigmoid”即逻辑激活函数,并且使用“binary_crossentropy”损失。
- 关于优化器,“sgd”表示我们使用简单的随机梯度下降来训练模型。使用SGD优化器时,调整学习率很重要。因此通常需要使用 optimizer=keras.optimizers.SGD(lr=???) 来设置学习率。
训练和评估模型
现在该模型已经准备好进行训练,我们只需要调用其fit()方法即可。
history = model.fit(X_train, y_train, epochs=30,
validation_data=(X_valid, y_valid))
- 如果训练集非常不平衡,其中某些类的代表过多,而其他类的代表不足,那么可以在调用 fit() 方法时设置 class_weight 参数。如果某些实例由专家标记,而另一些实例由众包平台标记,那么你可能希望为前者赋予更多的权重,可以设置 sample_weight 参数。
- fit() 方法返回一个 History 对象,其中包含训练参数(history.params)、经历的轮次(history.epoch),最重要的时包含 在训练集和验证集上 每个轮次结束时测得的损失和额外指标的字典(history.history)。可用次字典创建 pandas DataFrame 并调用其 plot() 方法。
import pandas as pd
import matplotlib.pyplot as plt
pd.DataFrame(history.history).plot(figsize=(8,5))
plt.grid(True)
plt.gca().set_ylim(0,1) # 设置竖轴范围为0~1
plt.show()
- 如果你对模型的性能不满意,则应回头调整超参数。首先要检查的是学习率。还可尝试另一个优化器,调整模型超参数(层数、每层神经元数以及用于每个隐藏层的激活函数类型)
- 对模型的验证精度感到满意后,应在测试集上对其进行评估泛化误差。
model.evaluate(X_test, y_test)
使用模型进行预测
比如使用测试集的前3个实例来预测:
X_new = X_test[:3]
y_proba = model.predict(X_new)
y_proba.round(2)
import numpy as np
# y_pred = model.predict_classes(X_new)
y_pred = np.argmax(model.predict(X_new), axis=-1)
print(y_pred)
print(np.array(class_names)[y_pred])
使用顺序API构建回归MLP
- 使用加州的人口普查数据建立起加州的房价模型,数据中有许多指标,诸如每个街区的人口数量、收入中位数、房价中位数等。模型需要从数据中学习,根据所有其他指标,预测任意区域的房价中位数。我们使用回归神经网络解决它。
- 我们使用 Scikit-Learn 的 fetch_california_housing() 函数加载数据,划分训练集、验证集和测试集,然后比例缩放所有的特征:
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()
X_train_full, X_test, y_train_full, y_test = train_test_split(
housing.data, housing.target)
X_train, X_valid, y_train, y_valid = train_test_split(
X_train_full, y_train_full)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)
- 由于数据集噪声很大,我们只使用比以前少的神经元的单层隐藏层,以避免过拟合:
import tensorflow as tf
from tensorflow import keras
model = keras.models.Sequential([
keras.layers.Dense(30, activation="relu", input_shape=X_train.shape[1:]),
keras.layers.Dense(1)
])
model.compile(loss="mean_squared_error", optimizer="sgd")
history = model.fit(X_train, y_train, epochs=20,
validation_data=(X_valid, y_valid))
mse_test = model.evaluate(X_test, y_test)
X_new = X_test[:3] # 假设这是3个新的实例
y_pred = model.predict(X_new)
print(y_pred)
使用函数式API构建复杂模型
- 非顺序神经网络的一个示例是“宽深”神经网络。它将所有或部分输入直接连接到输出层。
- 我们建立这样一个神经网络来解决加州的住房问题:
input_ = keras.layers.Input(shape=X_train.shape[1:])
hidden1 = keras.layers.Dense(30, activation="relu")(input_)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.Concatenate()([input_, hidden2])
output = keras.layers.Dense(1)(concat)
model = keras.Model(inputs=[input_], outputs=[output])
构建模型后,需要编译模型,对其进行训练,评估并使用它来进行预测。
- 如果你像通过宽路径送入特征的字迹,而通过深路径送入特征的另一个子集(可能有重合),可以使用多个输入。
import tensorflow as tf
from tensorflow import keras
input_A = keras.layers.Input(shape=[5], name="wide_input")
input_B = keras.layers.Input(shape=[6], name="deep_input")
hidden1 = keras.layers.Dense(30, activation="relu")(input_B)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
output = keras.layers.Dense(1, name="output")(concat)
model = keras.Model(inputs=[input_A, input_B], outputs=[output])
- 在编译模型的时候,我们调用 fit() 方法时,必须传递一对矩阵 (X_train_A, X_train_B).
model.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1e-3))
X_train_A, X_train_B = X_train[:, :5], X_train[:, 2:]
X_valid_A, X_valid_B = X_valid[:, :5], X_valid[:, 2:]
X_test_A, X_test_B = X_test[:, :5], X_test[:, 2:]
X_new_A, X_new_B = X_test_A[:3], X_test_B[:3]
history = model.fit((X_train_A, X_train_B), y_train, epochs=20,
validation_data=((X_valid_A, X_valid_B), y_valid))
mse_test = model.evaluate((X_test_A, X_test_B), y_test)
y_pred = model.predict((X_new_A, X_new_B))
- 添加额外的输出
更多推荐
所有评论(0)