Logistic Regression 代码实现

1,076 阅读4分钟

实验理论部分

Logistic Regression Model

image.png

Sigmoid function or logistic function 能够把假设函数的值控制在0-1之间,可以很好的应用在分类问题上,其表达式如上图所示,我们可以根据数据集找到合适的θ值契合此模型,下面我们通过举个例子说明模型的用法:

肿瘤是否为恶性的概率我们假设与肿瘤大小这一特征向量有关,假设我们现在知道了此例子中Logistic Regression 模型函数的θ值,通过将数据集中的数据带入得到假设函数hθ(x)=0.7,其代表的含义即为y=1的情况(恶性)的概率为70%,即P(y=1|x;θ)=0.7

image.png

image.png

决策边界

上面我们提到Logistic 回归假设函数:

hθ(x) = g(θTx) = P(y=1|x;θ) Sigmoid function: g(z) = 1/(1+e-z) 不难发现其函数图像特点,当Sigmoid function函数参数值z=0时hθ(x) =0.5,我们不妨这样假设,当hθ(x) >=0.5时,y=1,hθ(x) <0.5时,y=0,如下图

image.png

假设Sigmoid function参数值为θ01X12X2,这个数据集中参数theta我们假定如下:θ0 = -3, θ1 = 1,θ2 = 1

我们预测,y=1时,-3+x1+x2>=0,决策边界图像即:

image.png 边界线上的点代表的含义是hθ(x)=0.5

代价函数

image.png

image.png

上图我们用一个简单例子说明一下:如果最后y=1,我们预测P(y = 1|θ,x) = 0,(看图),那么我们将会付出很大的代价(无穷)

image.png 如果最后y = 0,我们预测P(y=1|θ,x) = 1,那么我们将付出很大的代价。

简化

需要注意的是我们讨论的上面y只有0,或1两种情况(二分类),因此,我们可以将上面的Cost(hθ(x(i),y(i))整合为下面的形式(可以代入y=1或y=0两种情况试一下)

-ylog(hθ(x))-(1-y)log(1-hθ(x))

这样我们就可以写出logistic回归的代价函数如下:

image.png

那么接下来我们又可以重写logistics回归中梯度下降算法了

image.png

image.png

上述模型算法实现

由上面内容明白了逻辑回归算法的原理,下面是代码实现部分:

首先,我们加载数据:

image.png

首先我们通过可视化查看特征项之间的关系:

数据集可视化

  • 前十个特征项与target(是否确诊为乳腺癌)之间关系图 image.png 点击查看大图
  • 11-20个特征项与target之间的关系 image.png 点击查看大图
  • 21-30个特征项与target之间的关系 image.png 点击查看大图 1.我们不难发现,通过上面30个特征项与target之间存在着明显的分类问题,而且是典型的二分类,因此可以用逻辑回归来解决。 2.另外,我们通过对比其他特征项之间的关系,我们发现有一些特征向量之间的相关性非常强,而且是正相关,我们通过下面的热点图便可以看出:
  • 前十个特征项之间的热点图 image.png 点击查看大图 这样我们可以通过线性回归求出特征项之间的关系,并且正确率会很高,这样可以有效减少数据集中的数据量。

另外,我们在加载出的数据集中数据可以看出,显然需要归一化处理。

数据加载与处理

为什么要归一化:

数据归一化问题是数据挖掘中特征向量表达时的重要问题,当不同的特征成列在一起的时候,由于特征本身表达方式的原因而导致在绝对数值上的小数据被大数据“吃掉”的情况,这个时候我们需要做的就是对抽取出来的features vector进行归一化处理,以保证每个特征被分类器平等对待。

(0, 1)归一化

  • 通过遍历feature vector里的每一个数据,将Max和Min的记录下来,并通过Max-Min作为基数(即Min=0,Max=1)进行数据的归一化处理

  • 数学公式

    31f8cf965475e709f0ae7d442aa5b1a0.png

  • 代码实现

# 数据归一化,采用(0,1)归一,将数据集中数据值归一到【0,1】区间
    def normalize_(self, x):
        offset = np.zeros(self.n_feature)
        scalar = np.ones(self.n_feature)
        for feature_idx in range(0, self.n_feature):
            col = x[:, np.newaxis, feature_idx]
            min = col.min()
            max = col.max()

            if (min != max):
                scalar[feature_idx] = 1.0 / (max - min)
            else:
                scalar[feature_idx] = 1.0 / max

            offset[feature_idx] = min
        //关键代码
        x = (x - offset) * scalar
        return x

训练模型

上面我们把数据集进行了简单的加载与处理,下面我们就应该对这些数据进行训练了。

Gradient Descent

按照实验理论部分,我们现在需要对梯度下降算法进行代码实现:

  • 激活函数
def sigmoid(self, z):
    e_part = np.exp(-z)
    return 1 / (1 + e_part)
  • 假设函数和假设与样本之间的距离
def hypotheticFun(self, x):
    z = np.dot(self.theta, x) + self.intercept
    return self.sigmoid(z)

def error_dist(self, x, y):
    return self.hypotheticFun(x) - y

  • 代价函数
# 代价函数
def Jfunction(self):
    sum = 0
    for i in range(0, self.m):
        h = self.hypotheticFun(self.x[i])
        sum += self.y[i] * np.log(h) + (1 - self.y[i]) * np.log(1 - h)
    return 1 / self.m * sum
  • 梯度下降算法求偏导数部分
# 梯度下降算法求偏导数部分
def partialDerivative(self, ):

    h = np.zeros(self.m)
    for i in range(0, self.m):
        h[i] = self.hypotheticFun(self.x[i])

    dist = h - self.y
    result = np.asarray(np.mat(dist.T) * self.x) / self.m
    return result