如何避免在逻辑回归的numpy实现中使用NaN?

时间:2018-08-17 15:25:43

标签: python-3.x numpy machine-learning nan logistic-regression

编辑:我已经取得了重大进展。我当前的问题是在我最后一次编辑之后写的,可以在没有上下文的情况下回答。

我目前在Coursera上关注Andrew Ng的Machine Learning Course,今天尝试实现logistic regression

符号:

  • X是一个(m x n)矩阵,其中输入变量的向量为行(m个变量的n-1训练样本,第一列的条目等于1到处代表一个常数)。
  • y是预期输出样本的相应向量(具有m项等于01的列向量)
  • theta是模型系数的向量(具有n个条目的行向量)

对于输入行向量x,模型将预测为阳性结果的概率sigmoid(x * theta.T)

这是我的Python3 / numpy实现:

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

vec_sigmoid = np.vectorize(sigmoid)

def logistic_cost(X, y, theta):
    summands = np.multiply(y, np.log(vec_sigmoid(X*theta.T))) + np.multiply(1 - y, np.log(1 - vec_sigmoid(X*theta.T)))
    return - np.sum(summands) / len(y)


def gradient_descent(X, y, learning_rate, num_iterations):
    num_parameters = X.shape[1]                                 # dim theta
    theta = np.matrix([0.0 for i in range(num_parameters)])     # init theta
    cost = [0.0 for i in range(num_iterations)]

    for it in range(num_iterations):
        error = np.repeat(vec_sigmoid(X * theta.T) - y, num_parameters, axis=1)
        error_derivative = np.sum(np.multiply(error, X), axis=0)
        theta = theta - (learning_rate / len(y)) * error_derivative
        cost[it] = logistic_cost(X, y, theta)

    return theta, cost

该实现似乎工作正常,但是在计算物流成本时遇到了问题。在某个时候,梯度下降算法收敛到非常合适的theta,并且发生以下情况:

对于某些具有预期结果X_i 1的输入行X * theta.T将变为正数,并具有良好的边距(例如23.207)。这将导致sigmoid(X_i * theta)变得完全 1.0000(这是因为我认为精度下降)。这是一个很好的预测(因为预期结果等于1),但是由于np.log(1 - vec_sigmoid(X*theta.T))的评估结果为NaN,因此这会中断物流成本的计算。这不应该是一个问题,因为该项会与1 - y = 0相乘,但是一旦出现NaN的值,整个计算就会中断(0 * NaN = NaN)。

由于np.multiply(1 - y, np.log(1 - vec_sigmoid(X*theta.T)))是在X的每一行中计算的(不仅在y = 0处),我应该如何处理?

示例输入:

X = np.matrix([[1. , 0. , 0. ],
               [1. , 1. , 0. ],
               [1. , 0. , 1. ],
               [1. , 0.5, 0.3],
               [1. , 1. , 0.2]])

y = np.matrix([[0],
               [1],
               [1],
               [0],
               [1]])

然后theta, _ = gradient_descent(X, y, 10000, 10000)(是的,在这种情况下,我们可以将学习率设置为 this 大),将theta设置为:

theta = np.matrix([[-3000.04008972,  3499.97995514,  4099.98797308]])

这将使vec_sigmoid(X * theta.T)成为以下各项的真正好预测:

np.matrix([[0.00000000e+00],      # 0
           [1.00000000e+00],      # 1
           [1.00000000e+00],      # 1
           [1.95334953e-09],      # nearly zero
           [1.00000000e+00]])     # 1

但是logistic_cost(X, y, theta)的值为NaN

编辑:

我想出了以下解决方案。我只是将logistic_cost函数替换为:

def new_logistic_cost(X, y, theta):
    term1 = vec_sigmoid(X*theta.T)
    term1[y == 0] = 1
    term2 = 1 - vec_sigmoid(X*theta.T)
    term2[y == 1] = 1
    summands = np.multiply(y, np.log(term1)) + np.multiply(1 - y, np.log(term2))
    return - np.sum(summands) / len(y)

通过使用蒙版,我只在结果将始终与零相乘的位置上计算log(1)。现在log(0)仅会在错误的梯度下降实现中发生。

悬而未决的问题:如何使该解决方案更干净?是否有可能以更清洁的方式达到类似的效果?

2 个答案:

答案 0 :(得分:0)

如果您不介意使用SciPy,则可以从type ErrorSpy struct { spies.Spy } func (spy *ErrorSpy) Get(code AccessCode) *ErrorSt { res := spy.Called(code) return res.Get(0).(*ErrorSt) } 导入expitxlog1py

scipy.special

并替换表达式

from scipy.special import expit, xlog1py

使用

np.multiply(1 - y, np.log(1 - vec_sigmoid(X*theta.T)))

答案 1 :(得分:0)

我知道这是一个老问题,但是我遇到了同样的问题,也许它将来可以帮助其他人,实际上我通过在附加X0之前对数据实施归一化来解决了这个问题。

def normalize_data(X):
    mean = np.mean(X, axis=0)
    std = np.std(X, axis=0)
    return (X-mean) / std

此后一切正常!