二元逻辑回归实现鸢尾花数据分类(python)

说明:

  • 本文利用python实现二元逻辑回归,没有加正则项。挑选iris数据前100个样本作为训练集,他们分属于两个类别,样本特征选择第1列(花萼长度x1)和第2列(花萼宽度x2)。
  • 程序以函数形式实现,附录中给出封装式程序实现。代码均以矩阵计算的思路编写。
  • 四部分程序是连续的,拷贝在一起就能完整运行。
  • 没考虑过拟合、欠拟合等问题。

1.查看鸢尾花数据集

from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import numpy as np

#%matplotlib

iris = load_iris() #导入鸢尾花数据
data = iris.data #numpy.ndarray
target = iris.target #numpy.ndarray

#print(data.shape) #查看维度(150,4),即150个样本,每个样本4个属性
#print(target.shape) #(150,)每个样本的类别0,1,2值,三个类型,前100个样本属于0和1类,后50个样本属于2类
#print(data) #查看完整数据
#print(target) #查看类型数据

#根据不同类型0,1,2和第0列(x1),第1列(x2)绘图。
plt.figure(1)
index_0 = np.where(target==0) #找出0所在位置
plt.scatter(data[index_0,0],data[index_0,1],marker='x',color = 'b',label = '0',s = 15)
index_1 =np.where(target==1) #找出1所在位置
plt.scatter(data[index_1,0],data[index_1,1],marker='o',color = 'r',label = '1',s = 15)
index_2 =np.where(target==2) #找出2所在位置
plt.scatter(data[index_2,0],data[index_2,1],marker='s',color = 'k',label = '2',s = 15) 
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend(loc = 'upper left')

上述程序的结果如下:


鸢尾花.png

2.二元逻辑回归程序实现

#定义二元逻辑回归需要的各个函数
def h_sigmod(W,X): #输出1×m
    g = np.dot(W,X.T) #1×m 计算h(X)
    return sigmod(g)

def sigmod(X):
    return 1/(1+np.exp(-X))

def train(X,y,learn_rate=0.001):
    #利用已知样本训练模型,得到系数
    W = None #存放系数
    Loss = [] #存放每次迭代的损失函数值

    num_train,num_feature = X.shape
    W = 0.001*np.random.randn(num_feature,1).reshape(1,-1) #初始化系数,并转化为1个行向量
    #print('W={}'.format(W))
    
    for i in range(5000):
        h = h_sigmod(W,X_train) #1×m
    
        #注意loss是一个标量,但是一个ndarray型数据,而非纯粹的int
        loss = -(np.log(h).dot(y) + np.log(1-h).dot(1-y)) 
    #    print(loss.shape)
        loss = loss/num_train
    
        dW = (y.T-h).dot(X_train) #计算(y-h(X))*X,实际上就是J(W)的一阶导数
    #    print('dW={}'.format(dW))
        W += learn_rate*dW #更新系数W=W+a(y-h(X))*X
    
        Loss.append(loss[0])
    #    print ('i={},Loss={}'.format(i,Loss[i]))
    
    return W,Loss

#利用训练得到的W系数对结果进行预测
def predict(W,X_test):
        h = h_sigmod(W,X_test) #利用训练得到的系数W计算h(X)结果就是预测值
        y_pred = np.where(h>=0.5,1,0)
        return y_pred

3.利用鸢尾花数据对模型进行训练得到系数

##利用鸢尾花部分数据训练模型并查看结果##############################################################
#从原始数据中抽取部分数据作为训练数据
# 前100个样本分别属于0和1类型,本例中用逻辑回归做二分类,不考虑花型数据为2类的数据。
X = data[0:100,[0,1]] #抽出第1和2个特征(x1和x2),分别是花萼的长和宽
y = target[0:100].reshape(-1,1) #每个样本对应的类型,0或1。转化成一个列向量。
# print(X[:5])
# print(y[-5:])

##增加一列“1”,表示x0列,这列只是为了与系数w的第一列w0相匹配(该列不需要与实际特征x1或x2相乘)
#该步很重要
one = np.ones((X.shape[0],1))
X_train = np.hstack((one,X))

W,Loss = train(X_train,y) #利用已知样本训练模型,得到系数和损失函数结果,()
print('预测的系数:[w0,w1,w2]={}'.format(W)) #输出训练得到的系数

plt.figure(2)
#print('W={}'.format(W))
plt.plot(Loss)
plt.xlabel('number of iteration')
plt.ylabel('Loss')
plt.show()

plt.figure(3)
label = np.array(y)
index_0 = np.where(label==0)
plt.scatter(X[index_0,0],X[index_0,1],marker='x',color = 'b',label = '0',s = 15)
index_1 =np.where(label==1)
plt.scatter(X[index_1,0],X[index_1,1],marker='o',color = 'r',label = '1',s = 15)
 
#show the decision boundary
x1 = np.arange(4,7.5,0.5)
x2 = (- W[0][0] - W[0][1]*x1) / W[0][2]
plt.plot(x1,x2,color = 'black')
plt.xlabel('X1')
plt.ylabel('X2')
plt.legend(loc = 'upper left')

##利用鸢尾花部分数据训练模型并查看结果

输出结果:

预测的系数:[w0,w1,w2]=[[-2.01401491  5.05730268 -8.19245827]]
损失函数.png

分类边界.png

4.利用前面得到的模型数据预测对其他数据进行分类

#预测测试一
#取X_train的前20个样本,让分类器对其进行预测,如果结果都是0,那么说明分类很准确。
X_test= X_train[:20,[0,1,2]]
result = predict(W,X_test) #根据预测得到的系数W对新的样本进行分类
print("测试一结果:{}".format(result)) #看输出是否为0
    
#预测测试二
#暂时抽取第2类数据的x1和x2特征来作为测试数据。
#从第一幅途中可以看出,仅从x1和x2两个特征看,第2类鸢尾花是属于第一1类的,即输出应该都是1
Test_data = data[100:,[0,1]] #暂时抽取第2类数据的x1和x2特征来作为测试数据
one = np.ones((Test_data.shape[0],1)) 
X_test = np.hstack((one,Test_data))
result = predict(W,X_test) #根据预测得到的系数W对新的样本进行分类
print("测试二结果:{}".format(result)) #看输出是否为1

输出分类结果:

测试一结果:[[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]] #全正确
测试二结果:[[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  1 1 1 1 1 1 1 1 1 1 1 1 1 1]] #全正确

附录:上述过程的封装式实现如下

# -*- coding: utf-8 -*-
"""
Created on Thu Mar  7 19:50:46 2019

@author: sean
"""

from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import numpy as np


# 多cell输出
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

#%matplotlib

iris = load_iris()
data = iris.data #numpy.ndarray
target = iris.target #numpy.ndarray

#print(data.shape) #查看维度(150,4),即150个样本,每个样本4个属性
#print(target.shape) #(150,)每个样本的类别0,1,2值,三个类型,前100个样本属于0和1类,后50个样本属于2类
#print(data) #查看完整数据
#print(target) #查看类型数据

#根据不同类型0,1,2和第0列(x1),第1列(x2)绘图。
plt.figure(1)
index_0 = np.where(target==0) #找出0所在位置
plt.scatter(data[index_0,0],data[index_0,1],marker='x',color = 'b',label = '0',s = 15)
index_1 =np.where(target==1) #找出1所在位置
plt.scatter(data[index_1,0],data[index_1,1],marker='o',color = 'r',label = '1',s = 15)
index_2 =np.where(target==2) #找出2所在位置
plt.scatter(data[index_2,0],data[index_2,1],marker='s',color = 'k',label = '2',s = 15) 
plt.xlabel('sepal length (x1)')
plt.ylabel('sepal width (x2)')
plt.legend(loc = 'upper left')

#准备数据############################################################################################
# 前100个样本分别属于0和1类型,本例中用逻辑回归做二分类,不考虑花型数据为2类的数据。
X = data[0:100,[0,1]] #抽出第1和2个特征(x1和x2),分别是花萼的长和宽
y = target[0:100].reshape(-1,1) #每个样本对应的类型,0或1。转化成一个列向量。
# print(X[:5])
# print(y[-5:])
##增加一列“1”,表示x0列,这列只是为了与系数w的第一列w0相匹配(该列不需要与实际特征x1或x2相乘)
one = np.ones((X.shape[0],1))
X_train = np.hstack((one,X))


#构造一个二元逻辑回归分类器########################################################################
class logistic(object):
    def __init__(self):
        self.W = None
        self.Loss = []
    def train(self,X,y,learn_rate = 0.001,num_iters = 5000):
        num_train,num_feature = X.shape
        #init the weight
        self.W = 0.001*np.random.randn(num_feature,1).reshape((1,-1))#初始化系数,并转化为1个行向量
        
        for i in range(num_iters):
            loss,dW = self.compute_loss(X,y)
#            print('i={},loss={}'.format(i,loss))
            
            self.W += learn_rate*dW #更新系数W=W+a(y-h(X))*X
            #print(self.W.shape)
            self.Loss.append(loss[0][0])
            
    def compute_loss(self,X,y):
        num_train = X.shape[0]
        h = self.h_sigmod(X) #1×m

        #注意loss是一个标量,但是一个ndarray型数据,而非纯粹的int
        loss = -(np.log(h).dot(y) + np.log(1-h).dot(1-y))
        #print(loss.shape)
        loss = loss/num_train

        dW = (y.T-h).dot(X_train) #计算(y-h(X))*X,实际上就是J(W)的一阶导数
        #    print('dW={}'.format(dW))
    
        return loss,dW

    def h_sigmod(self,X): #输出1×m
        g = np.dot(self.W,X.T) #1×m,计算h(X)
        return self.sigmod(g)
    
    def sigmod(self,X):
        return 1/(1+np.exp(-X))

    def predict(self,X): #利用训练得到的系数W计算h(X)结果就是预测值
        h = self.h_sigmod(X)
        y_pred = np.where(h>=0.5,1,0)
        return y_pred

#产生一个二元逻辑回归分类器对象######################################################################
my_logistic = logistic() #构造一个二元逻辑回归分类器
my_logistic.train(X_train,y) #利用数据X_train 和y对模型进行训练,确定W和Loss值
Loss = my_logistic.Loss
W = my_logistic.W
#print( 'W={}'.format(my_logistic.W) )
#print( 'Loss={}'.format(my_logistic.Loss) )

#查看损失函数
plt.figure(2)
plt.plot(Loss)
plt.xlabel('Iteration number')
plt.ylabel('Loss value')
plt.show()

##绘制根据二元逻辑回归得到的分界线
#将鸢尾花的前100个样本,根据x1和x2特征绘图
plt.figure(3)
label = np.array(y)
index_0 = np.where(label==0)
plt.scatter(X[index_0,0],X[index_0,1],marker='x',color = 'b',label = '0',s = 15)
index_1 =np.where(label==1)
plt.scatter(X[index_1,0],X[index_1,1],marker='o',color = 'r',label = '1',s = 15)
#根据训练得到的系数画分界线
x1 = np.arange(4,7.5,0.5)
x2 = (- W[0][0] - W[0][1]*x1) / W[0][2] 
plt.plot(x1,x2,color = 'black')
plt.xlabel('X1')
plt.ylabel('X2')
plt.legend(loc = 'upper left')

#利用训练得到的参数对新的样本进行分类
#预测测试一
#取X_train的前20个样本,让分类器对其进行预测,如果结果都是0,那么说明分类很准确。
X_test= X_train[:20,[0,1,2]]
result = my_logistic.predict(X_test) #对测试数据进行预测
print("测试一结果:{}".format(result)) #看结果有多少是0
    
#预测测试二
#暂时抽取第2类数据的x1和x2特征来作为测试数据。
#从第一幅途中可以看出,仅从x1和x2两个特征看,第2类鸢尾花是属于第一1类的,即输出应该都是1
Test_data = data[100:,[0,1]] 
one = np.ones((Test_data.shape[0],1)) 
X_test = np.hstack((one,Test_data))

result = my_logistic.predict(X_test)
print("测试二结果:{}".format(result)) #看输出是否为1
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容