课前:Feature Scaling
参见另一篇博客中的question1
Logistic Regression
虽然名字叫回归,实际是分类问题。比如下图,数据点分两个类别,希望得到的模型最终可以预测一个新的数据点的类别属于哪一类。
显然此类问题不希望模型去拟合数据点得到一条尽可能囊括更多数据点的拟合曲线,而是希望模型找到两类数据点之间的一个分界线。值得一提的是,logistic回归的分界线是一条直线,除了logistic,也有曲线做分界线的。
1. 确定模型:
既然要找一条直线分界线,我们假设决策边界可以表示为:
这就与线性回归的假设函数一致了,也是为什么分类问题的模型带“回归”两字的原因。
得到了分界线还不够,还要能判断类别,我们考虑引入区间[0,1],通过引入一个外层函数sigmoid,把这个假设函数归一化到结果在[0,1]之间,这样就可以进行类别判断了,比如结果大于0.5认为是class1,结果小于0.5认为是class2。
(1)sigmoid函数:
其函数曲线:
(2)逻辑回归的假设函数
至此,逻辑回归的假设函数得到了。
2. 确定损失函数
损失函数的计算是建立在极大似然估计的背景下的,极大似然是一个求最大的问题,损失函数是一个求最小的问题,二分类下class1和class2的极大似然表示如下:
(1)
以上两个式子,可以改写为一般形式:
(2)
根据极大似然估计可以得到:
(3)每类概率的乘积和
为了简化计算,取对数将得到:
(4)乘法变成了加法
我们希望极大似然越大越好
(5)
既然希望极大似然越大越好,对应就希望(5)越小越好,由此得到逻辑回归的损失函数:
(6)
要找到一组参数θ使得损失函数最小,通常采用梯度下降。
由此可见,逻辑回归的损失函数不是定义的,而是根据最大似然估计推导出来的。
3.多分类问题
One VS All:一对多
每次训练一类,其余剩下的类视为一个大类,如果有k个类别则需要训练k个模型;容易导致样本类不均衡。
One VS One:一对一
任意两类训练一个模型;训练次数过多,每两个类别要训练一个模型
交叉熵:
Softmax
softmax的形式如下:
多分类的概率计算如下:
引入指数函数形式:
对于不同类别zi,变成指数形式,求和求得概率pi,通过交叉熵计算分类
通过softmax多分类问题可如下解决:
代码
#logistic regrassion
import numpy as np
import matplotlib.pyplot as plt
import random
#创建数据集
data_0=np.random.multivariate_normal(mean=[3,4],cov=[[3,0],[0,1]],size=100)#均值,方差,个数,生成指定维度指定均值指定方差的多维度的变量
#2维,类别2类,类别取值只有两个也就是0和1
data_1=np.random.multivariate_normal(mean=[7,6],cov=[[3,0],[0,2]],size=200)
y_0 = np.array([0]*100)
y_1 = np.array([1]*200)
data_x = np.vstack((data_0,data_1)) #水平方向聚集
data_y = np.hstack((y_0,y_1)) #垂直方向聚集
plt.scatter(data_x[:,0],data_x[:,1],c=data_y)
plt.show()
#生成的数据过于一致,把数据点打乱
data=list(zip(data_x,data_y)) #打包,每个x和y建立联系
random.shuffle(data) #再随机打乱打包好的data
print(data)
#解包
data_x,data_y = zip(*data)
data_x = np.array(data_x)
data_y = np.array(data_y)
#----------------------------数据集创建完成-----------------------
#逻辑回归分开数据集
#第一步,建立模型 sigmoid套在线性函数外面
##先写sigmod
def sigmoid(x):
return np.array(1/(1+np.exp(-x)))
#写假设函数
def predict(w,b): #datax 300行两列
hidden = np.dot(data_x,w.T)+b #sigmoid内层的隐函数,其实就是线性回归的假设函数 x*w+b
return sigmoid(hidden)
#写梯度
def gradients(y_pred): #datay 的尺寸要进行维度处理 datay[:] 注意尺寸
#data_y:(300,0)->(300,1)
#y_pred:(300,1)
#data_x:(300,2)
grad_w = np.sum((y_pred-data_y[:,np.newaxis])*data_x,axis=0,keepdims=True) #data_y[:,np.newaxis].shape
grad_b = np.sum((y_pred-data_y[:,np.newaxis])*1,axis=0,keepdims=True) #b*x0,x0=1
# assert grad_w.shape==(1,2) #检查形状
# assert grad_b.shape==(1,1)
return grad_b,grad_w
#计算正确率
def acc(y_pred,threshold=0.5):
y_predict = np.around(y_pred) #around 四舍五入
correct_total = np.mean(np.equal(y_predict,data_y[:,np.newaxis])) #equal判断是否相等
return correct_total
#梯度下降
#指定学习率,参数初始化
learning_rate = 1e-2
iterations = 1000
w = np.random.rand(1,2)
b = np.random.rand(1,1)
from IPython import display
for i in range(iterations):
y_pred = predict(w,b)
grad_w,grad_b = gradients(y_pred)
w = w -learning_rate*grad_w
b = b-learning_rate*grad_b
if i%10 == 0:
w1 = w[0][0]
w2 = w[0][1]
b = b[0][0]
line_x = np.linspace(-10,20,100)
line_y = (0.5-b-w1*line_x)/w2
display.clear_output(wait=True)
plt.xlim((-5,15))
plt.ylim((0,10))
plt.plot(line_x,line_y)
plt.scatter(data_x[:,0],data_x[:,1],c=data_y)
plt.title(f'iteration:{i},acc:{round(acc(y_pred),3)}')
plt.pause(0.2)
print(acc(y_pred))