1.softmax的数学公式
1.1softmax的公式

1.2softmax的损失函数
softmax使用是损失函数是交叉熵(cross entropy),数学公式表现如下

也就是说,交叉熵只关心对正确类别的预测概率,因为只要其值足够大,就可以确保分类结果正确。当然,遇到一个样本有多个标签时,例如图像里含有不止一个物体时,我们并不能做这一步简化。但即便对于这种情况,交叉熵同样只关心对图像中出现的物体类别的预测概率。
假设训练数据集的样本数为nn,交叉熵损失函数定义为

1.3小结
总而言之,softmax是将最后一层所得到的结果,进行转换成总和为1的概率问题。
2.softmax的python代码
2.1 导入库
import tensorflow as tf
import numpy as np
print(tf.__version__)
↑基本模块tensorflow跟numpy的导入
from tensorflow.keras.datasets import fashion_mnist
batch_size=256
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
x_train = tf.cast(x_train, tf.float32) / 255 #在进行矩阵相乘时需要float型,故强制类型转换为float型
x_test = tf.cast(x_test,tf.float32) / 255 #在进行矩阵相乘时需要float型,故强制类型转换为float型
train_iter = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(batch_size)
test_iter = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(batch_size)
↑这里很好理解就是定义train 跟 test 的数据来源与类型
num_inputs = 784
num_outputs = 10
W = tf.Variable(tf.random.normal(shape=(num_inputs, num_outputs), mean=0, stddev=0.01, dtype=tf.float32))
b = tf.Variable(tf.zeros(num_outputs, dtype=tf.float32))
↑初始化模块参数,因为minst-fashion的图像都是28x28的所以作为为全连接层,就是784个节点,output就是指最后输出的10个类别的参数。
def softmax(logits, axis=-1):
return tf.exp(logits)/tf.reduce_sum(tf.exp(logits), axis, keepdims=True)
↑定义softmax的运算:为了更好理解代码就将上面公式拿过来

tf.exp(logits):是给logic进行exp运算就是log
tf.reduce_sum()

我们可以得知就是进行上面式子的操作
def net(X):
logits = tf.matmul(tf.reshape(X, shape=(-1, W.shape[0])), W) + b
return softmax(logits)
↑定义模型:tf.reshape 是将X,转换成W的行数,前面的-1表示函数自动计算列数。这一步是为了消除x与w矩阵不匹配的问题。
shape[0] = = >行数
shape[1] = =>列数
而tf.matmul函数就是进行X*W的矩阵运算。
def cross_entropy(y_hat, y):
y = tf.cast(tf.reshape(y, shape=[-1, 1]),dtype=tf.int32)
y = tf.one_hot(y, depth=y_hat.shape[-1])
y = tf.cast(tf.reshape(y, shape=[-1, y_hat.shape[-1]]),dtype=tf.int32)
return -tf.math.log(tf.boolean_mask(y_hat, y)+1e-8)
↑定义损失函数:
假设y=[0,2] 一行两列 y_hat=[[0.1,0.3,0.6] [0.3,0.2,0.5]] 两行三列
第一步
先进行数据类型的转换,转换成int类型,并且用reshape(-1,1)函数将y变成N行一列的矩阵。 y=[[0],[2]]两行一列
第二步
进行one_hot,首先进行depth的计算,y_hat.shape[-1]指定是列数就是3所以depth=3。式子就变成了y=th.one_hot(y,3)
而 one_hot是返回一个张量,这里如果不好理解可以查阅该函数的解释。这里就不多介绍了
所以y就变成一个2x1x3的矩阵,并进行了热编码为[[1,0,0][0,0,1]],==这是二维的向量==
第三步
shape=[-1, y_hat.shape[-1]])中y_hat.shape[-1]为3,所以最后将y变成n行3列,用我假设的矩阵的话y就是([1,0,0][0,0,1])==这是一维的向量==
第四步
进行计算tf.boolean_mask(y_hat, y),这里可以理解为矩阵运算,得到的结果就是([0.1][0.5]),然后再+1e-8,在进行log计算
def accuracy(y_hat, y):
return np.mean((tf.argmax(y_hat, axis=1) == y))
↑这里是进行数据预测,最后的结果与实际结果是否相吻合。简而言之那上述举得例子。y=[0,2] 一行两列 y_hat=[[0.1,0.3,0.6] [0.3,0.2,0.5]] 两行三列
那么得到的结果就是0.5的准确率应为y_hat 的最大之因该为[2,2]。
# 描述,对于tensorflow2中,比较的双方必须类型都是int型,所以要将输出和标签都转为int型
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for _, (X, y) in enumerate(data_iter):
y = tf.cast(y,dtype=tf.int64)
acc_sum += np.sum(tf.cast(tf.argmax(net(X), axis=1), dtype=tf.int64) == y)
n += y.shape[0]
return acc_sum / n
↑这里是我们可以评价模型net在数据集data_iter上的准确率。
#完整的训练数据集
num_epochs, lr = 5, 0.1
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None, trainer=None):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
with tf.GradientTape() as tape:
y_hat = net(X)
l = tf.reduce_sum(loss(y_hat, y))
grads = tape.gradient(l, params)
if trainer is None:
# 如果没有传入优化器,则使用原先编写的小批量随机梯度下降
for i, param in enumerate(params):
param.assign_sub(lr * grads[i] / batch_size)
else:
# tf.keras.optimizers.SGD 直接使用是随机梯度下降 theta(t+1) = theta(t) - learning_rate * gradient
# 这里使用批量梯度下降,需要对梯度除以 batch_size, 对应原书代码的 trainer.step(batch_size)
trainer.apply_gradients(zip([grad / batch_size for grad in grads], params))
y = tf.cast(y, dtype=tf.float32)
train_l_sum += l.numpy()
train_acc_sum += tf.reduce_sum(tf.cast(tf.argmax(y_hat, axis=1) == tf.cast(y, dtype=tf.int64), dtype=tf.int64)).numpy()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'% (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
trainer = tf.keras.optimizers.SGD(lr)
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)
↑
#预测结果
import matplotlib.pyplot as plt
X, y = iter(test_iter).next()
def get_fashion_mnist_labels(labels):
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
return [text_labels[int(i)] for i in labels]
def show_fashion_mnist(images, labels):
# 这⾥的_表示我们忽略(不使⽤)的变量
_, figs = plt.subplots(1, len(images), figsize=(12, 12)) # 这里注意subplot 和subplots 的区别
for f, img, lbl in zip(figs, images, labels):
f.imshow(tf.reshape(img, shape=(28, 28)).numpy())
f.set_title(lbl)
f.axes.get_xaxis().set_visible(False)
f.axes.get_yaxis().set_visible(False)
plt.show()
true_labels = get_fashion_mnist_labels(y.numpy())
pred_labels = get_fashion_mnist_labels(tf.argmax(net(X), axis=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
show_fashion_mnist(X[0:9], titles[0:9])
↑
3.softmax的keras代码
import tensorflow as tf
from tensorflow import keras
fashion_mnist = keras.datasets.fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0
model = keras.Sequential([
keras.layers.Flatten(input_shape=(28, 28)),
keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer=tf.keras.optimizers.SGD(0.1),
loss = 'sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train,y_train,epochs=5,batch_size=256)
test_loss, test_acc = model.evaluate(x_test, y_test)
print('Test Acc:',test_acc)
这里代码就不进行过多的叙述,用kreas这种高度集合的框架确实方便。但是这种对日后代码具体调试是极为不利的。
这是自己很早之前写的,copy过来偷个懒