“Salute!”从零开始softmax的实现-tensorflow
-
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过来偷个懒 -
@153-2211 还是有点不够清楚