全连接网络,实质上是多层感知器,作为分类器出现在应用中。
单层感知器是一个简单的线性分类器,能够处理线性二分类问题。
多层感知器由单层感知器组合而成,能够处理非线性多分类问题。
表达式如下:
y = f ( ∑ i = 1 n w i x i + b ) y=f(\sum_{i=1}^{n} w_ix_i+b) y=f(i=1∑nwixi+b)
其中, w w w是权重, b b b是偏置, x x x是输入(特征,可以是多个), y y y是输出(对于2分类 y y y是0或者1), f f f是激活函数。
激活函数(是一个阶跃函数)表达是如下:
f ( x ) = { 1 , x >0 0 , otherwise f(x)= \begin{cases} 1, & \text{$x$>0} \\ 0, & \text{otherwise}\\ \end{cases} f(x)={1,0,x>0otherwise
单层感知器的训练方法是梯度下降法。
它是由单层感知器组合形成的,后一层节点的输入来自前一层所有节点的输出,即层之间全部连接,所以叫全连接网络。网络结构图如下:
包含:输入层、隐藏层(有多个)、输出层。
其中每个蓝色点是一个神经元,每个神经元的计算和单层感知器一样。
激活函数采用了sigmoid函数,输出值范围0-1,相对于阶跃函数,它使得分类的超平面从线性到非线性。表达式如下:
f ( x ) = 1 1 + e x f(x)=\frac {1} {1+e^x} f(x)=1+ex1
sigmoid的曲线:
实现手写体的网络结构大致如下:
输入一张数字图片灰度图像,图片大小28*28,包含784个像素值。
经过全连接网络后,输出10个数据(10个数据计算sortmax转为概率),即最后输出10个概率值,分别对应0-9数字图像的10类别。
MNIST是一个入门级的计算机视觉数据集,它包含各种手写数字图片,也包含了每一张图像的标签;MNIST数据集分为训练数据集和测试数据集两部分(数据集的数据都是由图片数据集和对应的标签数据集组成)。
其下载地址http://yann.lecun.com/exdb/mnist/ :
其中包含4个文件:
(1) 训练集:用于训练的数据。
1. train-images-idx3-ubyte.gz: 包含6万张2828图片。
2. train-labels-idx1-ubyte.gz:包含6万张图片的标签,即每一张是什么数字。
(2) 测试集:用于测试模型的泛化能力。
1. t10k-images-idx3-ubyte.gz: 包含1万张2828图片。
2. t10k-labels-idx1-ubyte.gz:包含1万张图片的标签,即每一张是什么数字。
# data_manager.py
import struct
import os
import numpy as np
import gzip
def load_images(filename):
"""load images
filename: the name of the file containing data
return -- a matrix containing images as row vectors
"""
g_file = gzip.GzipFile(filename)
data = g_file.read()
magic, num, rows, columns = struct.unpack('>iiii', data[:16])
dimension = rows*columns
X = np.zeros((num,rows,columns), dtype='uint8')
offset = 16
for i in range(num):
a = np.frombuffer(data, dtype=np.uint8, count=dimension, offset=offset)
X[i] = a.reshape((rows, columns))
offset += dimension
return X
def load_labels(filename):
"""load labels
filename: the name of the file containing data
return -- a row vector containing labels
"""
g_file = gzip.GzipFile(filename)
data = g_file.read()
magic, num = struct.unpack('>ii', data[:8])
d = np.frombuffer(data,dtype=np.uint8, count=num, offset=8)
return d
def load_data(foldername):
"""加载MINST数据集
foldername: the name of the folder containing datasets
return -- train_X训练数据集, train_y训练数据集对应的标签,
test_X测试数据集, test_y测试数据集对应的标签
"""
# filenames of datasets
train_X_name = "train-images-idx3-ubyte.gz"
train_y_name = "train-labels-idx1-ubyte.gz"
test_X_name = "t10k-images-idx3-ubyte.gz"
test_y_name = "t10k-labels-idx1-ubyte.gz"
train_X = load_images(os.path.join(foldername, train_X_name))
train_y = load_labels(os.path.join(foldername,train_y_name))
test_X = load_images(os.path.join(foldername, test_X_name))
test_y = load_labels(os.path.join(foldername, test_y_name))
return train_X, train_y, test_X, test_y
网络这里搭建3层,2个隐层神经元分别为128和64,1个输出层神经元为类别数。
# network/mydense.py
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
class MNIST_FcNet(keras.Model):
def __init__(self, num_classes=10):
""" 搭建网络的层 """
super(MNIST_FcNet, self).__init__()
# 隐藏层第1层 -- 128个神经元,激活函数sigmoid, 这个层要有偏置
self.fc1 = layers.Dense(128, activation="sigmoid", use_bias=True)
# 隐藏层第2层 -- 64个神经元,激活函数sigmoid, 这个层要有偏置
self.fc2 = layers.Dense(64, activation="sigmoid", use_bias=True)
# 输出层 -- 64个神经元,激活函数sigmoid, 这个层要有偏置
self.out = layers.Dense(num_classes, activation="sigmoid", use_bias=True)
# 定义一个soft层,计算输出层的数据转为概率形式。
self.sm = layers.Softmax()
def call(self, x):
""" 数据运算流程 """
x = self.fc1(x)
x = self.fc2(x)
x = self.out(x)
x = self.sm(x)
return x
按照tensorflow2.x读取batch的方式:
def process_image(image, label):
""" 图片预处理 """
m = image.shape[0] * image.shape[1]
image = tf.reshape(image, (m,))
label = tf.one_hot(label, depth=10)
return image, label
def get_dataset(X, Y, is_shuffle=False, batch_size=64):
ds = tf.data.Dataset.from_tensor_slices((X, Y))
ds = ds.map(process_image)
ds = ds.shuffle(buffer_size=1024)
ds = ds.batch(batch_size)
return ds
在类的初始化函数中定义好模型、优化器、损失函数等。
在train函数实现训练过程。
import os
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras import layers,losses, metrics
from data_manager import load_data
from network.mydense import MNIST_FcNet
class TrainModel():
def __init__(self, lr=0.1):
self.model = MNIST_FcNet(num_classes=10) # 定义网络
self.model.build(input_shape=(None, 784))
self.model.summary()
self.loss_fun = losses.CategoricalCrossentropy() # 定义损失函数, 这里交叉熵
self.opt = tf.optimizers.SGD(learning_rate=lr) # 随机梯度下降优化器
# 设定统计参数
self.train_acc_metric = metrics.CategoricalAccuracy()
self.val_acc_metric = metrics.CategoricalAccuracy()
def train(self, fpath="./data/MNIST", epochs=500, m=50):
""" 训练网络 """
batch_size = 64
test_acc_list = []
# 读取数据集
train_X, train_y, test_X, test_y = load_data(fpath)
train_dataset = get_dataset(train_X, train_y, is_shuffle=True, batch_size=batch_size)
val_dataset = get_dataset(test_X, test_y, is_shuffle=False, batch_size=batch_size)
# 训练
loss_val = 0
for epoch in range(epochs):
print(" ** Start of epoch {} **".format(epoch))
# 每次获取一个batch的数据来训练
for nbatch, (inputs, labels) in enumerate(train_dataset):
with tf.GradientTape() as tape: # 开启自动求导
y_pred = self.model(inputs) # 前向计算
loss_val = self.loss_fun(labels, y_pred) # 误差计算
grads = tape.gradient(loss_val, self.model.trainable_variables) # 梯度计算
self.opt.apply_gradients(zip(grads, self.model.trainable_variables)) # 权重更新
# 更新统计传输
self.train_acc_metric(labels, y_pred)
# 打印
if nbatch % m == 0:
correct = tf.equal(tf.argmax(labels, 1), tf.argmax(y_pred, 1))
acc = tf.reduce_mean(tf.cast(correct, tf.float32))
print('{}-{} train_loss:{:.5f}, train_acc:{:.5f}'.format(epoch, nbatch, float(loss_val), acc))
# 输出统计参数的值
train_acc = self.train_acc_metric.result()
self.train_acc_metric.reset_states()
print('Training acc over epoch: {}, acc:{:.5f}'.format(epoch, float(train_acc)))
# 每次迭代在验证集上测试一次
for nbatch, (inputs, labels) in enumerate(val_dataset):
y_pred = self.model(inputs)
self.val_acc_metric(labels, y_pred)
val_acc = self.val_acc_metric.result()
self.val_acc_metric.reset_states()
print('Valid acc over epoch: {}, acc:{:.5f}'.format(epoch, float(val_acc)))
test_acc_list.append(val_acc)
if loss_val < 0.001:
print("*********************** loss_val: {}".format(loss_val))
break
# 训练完成保存模型
tf.saved_model.save(self.model, "./output/mnist_model")
# 画泛化能力曲线(横坐标是epoch, 测试集上的精度),并保存
x = np.arange(1, len(test_acc_list)+1, 1)
y = np.array(test_acc_list)
plt.plot(x, y)
plt.xlabel("epoch")
plt.ylabel("val_acc")
plt.title('model acc in valid dataset')
plt.savefig("./output/val_acc.png", format='png')
if __name__ == "__main__":
path = "./output"
if not os.path.exists(path):
os.makedirs(path)
model = TrainModel()
model.train()
我训练到140个批次作业,精度不在上升了。
训练集合上的的精度:96%,验证acc:95.5%
# predict_model.py
import os
import numpy as np
import tensorflow as tf
import cv2
import matplotlib.pyplot as plt
def handel_image(img_dir):
image = cv2.imread(img_dir)
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 将图片转化成黑白图片
image = cv2.resize(image, dsize=(28, 28)) # 裁剪图片成28*28大小
x_img = np.reshape(image, [-1, 784]) # 将图像reshape:[-1,784]形式
return x_img
if __name__ == "__main__":
# 1. 加载模型
model = tf.saved_model.load("./output/mnist_model")
# 2. 图像预处理
imag_data = handel_image("./data/test_image_300x300.png")
# 3. 测试
out = model(imag_data)
cls = int(tf.argmax(out, 1))
confidence = out[0][cls]
print("类别: {}, 置信度:{}".format(cls, confidence))
大家如果觉得有帮助,不要忘记点赞、收藏、关注哦 ~~~~~~