入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删。
目录
一、softmax的理论部分
1、softmax提出的原因
2、softmax公式
3、常搭配的损失函数——交叉熵损失函数
(1)分类要求
(2)引入原因
(3)公式
二、softmax的代码实现
1、自行实现
(1)全连接层和softmax层定义
(2)网络部分
(3)主函数部分
(4)结果
?2、sklearn实现
一、softmax的理论部分
1、softmax提出的原因
利用回归模型来做分类问题的时候,虽然可以将预测值就近定点化到固定离散值之一(类别),但这种连续值到离散值的转化会影响分类的效果。因此提出softmax作为最后部分的概率输出,将输出的值控制在0到1之间,代表属于每个类别的概率。
2、softmax公式
(其中n为批量数,d为特征数,c为类别数)
即每行是一个样本,分母是每行的所有类别得分之和,分子是该样本的每种类别的得分,这样就能得到每个样本所属每个类别的概率值。
3、常搭配的损失函数——交叉熵损失函数
(1)分类要求
不求每类概率的精准性,只求每类概率间的大小比较能突出那个正确类别的概率(即要求正确类别概率最大)
(2)引入原因
平方损失函数过于严格的要求每类概率的精准性,所以不适合,因此引入了交叉熵损失函数。
(3)公式
其中即样本真实的类别概率(所属类别概率为1,其余为0),为预测样本的类别概率(第i个样本第j类的概率)。
即实现了只关心正确的概率,目标:尽可能地让他变小。
同时也等价于:(最大化)
二、softmax的代码实现
1、自行实现
(1)全连接层和softmax层定义
import numpy as np
####################全连接网络定义部分########################
class FullyConnectedLayer(object):
def __init__(self, num_input, num_output): # 全连接层初始化
self.num_input = num_input
self.num_output = num_output
# 参数初始化
self.w = np.random.normal(loc=0.0, scale=0.01, size=(self.num_input, self.num_output))#W(d,c)
self.b = np.zeros([1, self.num_output])#B(1,c)
def forward(self, input): # 前向传播计算
self.input = input#X(n,d)
# 全连接层的前向传播,计算输出结果
self.output = np.matmul(self.input,self.w)+self.b#利用矩阵乘法(Y=XW+B)——(n,c)
return self.output
def backward(self, top_diff,lr): # 反向传播的计算
#全连接层的反向传播,计算参数梯度和本层损失
self.d_weight = np.matmul(self.input.T,top_diff)#Y对W求导为X^T,再乘上一个梯度
self.d_bias = top_diff#Y对B求导为全1矩阵
bottom_diff = np.matmul(top_diff,self.w.T)#Y对X(输入,即上一层的输出)求导为W^T
self.update_param(lr)
return bottom_diff
def update_param(self, lr): # 参数更新
# TODO:对全连接层参数利用参数进行更新
self.w = self.w - lr*self.d_weight
self.b = np.reshape(np.average(self.b - lr*self.d_bias, axis=0),(1, self.num_output))#从(n,c)重新变为(1,c)
def load_param(self, weight, bias): # 参数加载
assert self.w.shape == weight.shape
assert self.b.shape == bias.shape
self.w = weight
self.b = bias
def save_param(self): # 参数保存
return self.w, self.b
####################全连接网络定义部分########################
####################softmax网络定义部分#######################
class SoftmaxLossLayer(object):
def __init__(self):
pass
def forward(self, input): # 前向传播的计算
# TODO:softmax 损失层的前向传播,计算输出结果
#input_max = np.max(input, axis=1, keepdims=True)#生成(n,1)大小的每行的最大值
input_exp = np.exp(input)#生成(n,class)大小的e^(每个类别的概率)
sum_denominator = np.sum(input_exp,axis=1,keepdims=True)#取每行的和,变成(n,1)
self.prob = input_exp / sum_denominator#得到softmax最终公式(即属于每一类的概率),分母(n,1)拓展成(n,c)
#print(self.prob.shape)
return self.prob
def get_loss(self, label): # 计算损失(这边要的是一个确切的数值结果)
self.batch_size = self.prob.shape[0]#n
#构建真实label
self.label_onehot = np.zeros_like(self.prob)
self.label_onehot[np.arange(self.batch_size), label] = 1.0#第几个样本最终属于哪一类(概率为1,其他为0)
loss = -np.sum(np.log(self.prob) * self.label_onehot) / self.batch_size
return loss
def backward(self): # 反向传播的计算
# TODO:softmax 损失层的反向传播,计算本层损失(这边要的是一个梯度矩阵)
bottom_diff = (self.prob - self.label_onehot)/self.batch_size
return bottom_diff
####################softmax网络定义部分#######################
(2)网络部分
import random
import numpy as np
from layer_softmax_linear import SoftmaxLossLayer,FullyConnectedLayer
#读取数据集
def data_iter(batch_size,num_examples,features,lables):
index=list(range(num_examples))#获取每个样本的索引
random.shuffle(index)#使索引顺序变成随机的,即样本读取顺序随机
for i in range(0,num_examples,batch_size):#每次读取batch_size数据量出来
j=np.array(index[i:min(i+batch_size,num_examples)])#获取这次小批量样本的索引
yield features[j],lables[j]
#模型
class Model(object):
def __init__(self,num_examples,num_input,num_output,batch_size,num_epochs,lr):
self.num_examples=num_examples
self.batch_size=batch_size
self.num_epochs=num_epochs
self.lr=lr
self.linear=FullyConnectedLayer(num_input, num_output)
self.softmax=SoftmaxLossLayer()
def forward(self,input):#前向传播
layer1=self.linear.forward(input)
self.output=self.softmax.forward(layer1)
return self.output
def backward(self):#后向传播
bottom_diff=self.softmax.backward()
self.linear.backward(bottom_diff,self.lr)
def get_loss(self,label):#得到损失loss
self.loss=self.softmax.get_loss(label)
return self.loss
def train(self,features,labels):#训练
for epoch in range(self.num_epochs):
loss=0
for x,y in data_iter(self.batch_size,self.num_examples,features,labels):
self.forward(x)#前向传播
loss=loss+self.get_loss(y)#得到一次batchsize的loss
self.backward()#后向传播
loss=loss/(self.num_examples//self.batch_size)
print('epoch'+str(epoch+1)+', loss:'+str(loss))
def parms(self):#返回模型参数
return self.linear.save_param()
def test(self,input):#用训练好的模型测试样本,返回标签和各类别的概率
layer1=self.linear.forward(input)
output=self.softmax.forward(layer1)
label=np.argmax(output,axis=1).reshape(-1,1)#求每行最大值的索引,对应预测的类
return label,output
def evaluate(self,features,labels):#验证
label,_=self.test(features)
accuracy = np.mean(label==labels.reshape(-1,1))
print("准确率为:",accuracy)
(3)主函数部分
import numpy as np
from sklearn import datasets
from model import Model
##########################数据准备部分##########################
iris = datasets.load_iris() # 导入鸢尾花数据集
features = iris.data # 特征集
labels = iris.target # label集
##########################数据准备部分##########################
##########################模型构建部分##########################
#参数定义部分
num_examples=features.shape[0]#样本数
num_inputs=features.shape[1]#特征数目
num_outputs=3#类别数
batch_size=15
num_epochs=100
lr=0.01
net=Model(num_examples,num_inputs,num_outputs,batch_size,num_epochs,lr)
net.train(features,labels)
net.evaluate(features,labels)
parms=net.parms()
print(parms)
(4)结果
?2、sklearn实现
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
#数据准备
iris = datasets.load_iris()
x,y=iris.data,iris.target
x_train,x_test,y_train,y_test = train_test_split(x,y)
#数据标准化
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.fit_transform(x_test)
#模型训练
model = LogisticRegression()
model.fit(x_train,y_train)
print("参数W为:",model.coef_)
print("参数b为:",model.intercept_)
y_pred = model.predict(x_test)
print("测试集准确率为:",accuracy_score(y_pred,y_test))
结果:
欢迎大家在评论区批评指正,谢谢大家~
|