正则化
- 正则化主要是为了减少模型的过拟合,就像激活函数是为了增加网络的非线性。
- Dropout只是其中的一种方法,主要是以一个概率将隐藏层的隐藏单元置为0
3. 通常越浅层网络置0的概率越低。
import tensorflow as tf
from d2l import tensorflow as d2l
# 正则化 以dropout的概率丢弃张量输入X中的元素
def dropout_layer(X, dropout):
# 断言 判断dropout是否在0-1
assert 0 <= dropout <= 1
# 概率为1时全部丢弃
if dropout == 1:
return tf.zeros_like(X)
# 概率为0时不丢弃任何元素
if dropout == 0:
return X
# tf.random.uniform产生的是大小与X一致的张量,里面的元素在0-1之间
# mask张量里面的值全是0或1,实现了丢弃概率为dropout
mask = tf.random.uniform(
shape = tf.shape(X), minval=0, maxval=1) < 1-dropout
# mask里的元素为1,对应的X元素不变,mask为0,X的元素对应变成0
# 除以(1-dropoup)是为了保持期望不变,假设X有n个元素,期望为E
# 所以变化前总的为 nE
# 变化后只有(1-dropout)n个元素有值 (1-dropout)nE
# 右边除掉(1-dropout)均值就相同了,当然不要想着完全相同,毕竟概率这种事情
return tf.cast(mask, dtype=tf.float32) * X / (1.0 - dropout)
# 定义模型参数
num_outputs, num_hiddens1, num_hiddens2 = 10, 256, 256
# 定义模型
# 设置dropout概率
dropout1, dropout2 = 0.2, 0.5
class Net(tf.keras.Model):
# 初始化定义网络层,3层网络
def __init__(self, num_outputs, num_hiddens1, num_hiddens2):
super().__init__()
# 输入层把数据拉成一维
self.input_layer = tf.keras.layers.Flatten()
# 定义第一层隐藏层
self.hidden1 = tf.keras.layers.Dense(num_hiddens1, activation='relu')
# 定义第2层隐藏层
self.hidden2 = tf.keras.layers.Dense(num_hiddens2, activation='relu')
# 定义输出层
self.output_layer = tf.keras.layers.Dense(num_outputs)
def call(self, inputs, training=None):
x = self.input_layer(inputs)
x = self.hidden1(x)
# 只有在训练模型时才使用dropout
if training:
# 在第一个全连接层之后添加一个dropout层
x = dropout_layer(x, dropout1)
x = self.hidden2(x)
if training:
# 在第二个全连接层之后添加一个dropout层
x = dropout_layer(x, dropout2)
x = self.output_layer(x)
return x
# 初始化模型
net = Net(num_outputs, num_hiddens1, num_hiddens2)
# 训练和测试,需要把前面的acc函数复制过来了
# 有SGD优化函数了,就不用复制那个updataer了
num_epochs, lr, batch_size = 10, 0.5, 256
# 损失函数
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
# 载入数据
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
# 优化器
trainer = tf.keras.optimizers.SGD(learning_rate=lr)
# acc和训练函数
# 分类精度 统计正确的数量 精度:accuracy(y_hat, y) / len(y)
def accuracy(y_hat, y):
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
y_hat = tf.argmax(y_hat, axis=1)
cmp = tf.cast(y_hat, y.dtype) == y
return float(tf.reduce_sum(tf.cast(cmp, y.dtype)))
def evaluate_accuracy(net, data_iter):
metric = Accumulator(2) # 正确预测数、预测总数
for X, y in data_iter:
metric.add(accuracy(net(X), y), d2l.size(y))
return metric[0] / metric[1]
class Accumulator:
def __init__(self, n):
self.data = [0.0] * n
def add(self, *args):
self.data = [a + float(b) for a, b in zip(self.data, args)]
def reset(self):
self.data = [0.0] * len(self.data)
def __getitem__(self, idx):
return self.data[idx]
# 训练
# updater是更新模型参数的常用函数
def train_epoch_ch3(net, train_iter, loss, updater):
# 训练损失总和、训练准确度总和、样本数, 累积求和
metric = Accumulator(3)
for X,y in train_iter:
# 计算梯度并更新参数
with tf.GradientTape() as tape:
y_hat = net(X)
# Keras内置的损失接受的是(标签,预测),这不同于用户在本书中的实现。
# 本书的实现接受(预测,标签),例如我们上面实现的“交叉熵”
if isinstance(loss, tf.keras.losses.Loss):
l = loss(y, y_hat)
else:
l = loss(y_hat, y)
if isinstance(updater, tf.keras.optimizers.Optimizer):
params = net.trainable_variables
grads = tape.gradient(l, params)
updater.apply_gradients(zip(grads, params))
else:
updater(X.shape[0], tape.gradient(l, updater.params))
# Keras的loss默认返回一个批量的平均损失
l_sum = l * float(tf.size(y)) if isinstance(
loss, tf.keras.losses.Loss) else tf.reduce_sum(l)
metric.add(l_sum, accuracy(y_hat, y), tf.size(y))
# 返回训练损失和训练精度
return metric[0] / metric[2], metric[1] / metric[2]
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
# """训练模型(定义见第3章)"""
for epoch in range(num_epochs):
train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
test_acc = evaluate_accuracy(net, test_iter)
train_loss, train_acc = train_metrics
print("Epoch %s/%s:"%(epoch,num_epochs)+" train_loss: "+str(train_loss) + " train_acc: "+str(train_acc) + " test_acc: "+str(test_acc))
# 训练
train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
训练过程:
与前面的内容大致一样,只是多了dropout层
简易实现就i是直接用接口了,
把net换一下就好了
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(256, activation=tf.nn.relu),
# 在第一个全连接层之后添加一个dropout层
tf.keras.layers.Dropout(dropout1),
tf.keras.layers.Dense(256, activation=tf.nn.relu),
# 在第二个全连接层之后添加一个dropout层
tf.keras.layers.Dropout(dropout2),
tf.keras.layers.Dense(10),
])