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

s16*_*210 5 numpy machine-learning nan python-3.x logistic-regression

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

我目前在Coursera上关注Andrew Ng的机器学习课程,并尝试在今天实施逻辑回归.

符号:

  • X是一个(m x n)带有输入变量向量作为行的矩阵(m训练n-1变量样本,第一列的条目在任何地方都等于1表示一个常量).
  • y是预期输出样本的相应向量(m条目向量等于0或的列向量1)
  • 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
Run Code Online (Sandbox Code Playgroud)

这个实现似乎工作正常,但我在计算logistic-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]])
Run Code Online (Sandbox Code Playgroud)

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

theta = np.matrix([[-3000.04008972,  3499.97995514,  4099.98797308]])
Run Code Online (Sandbox Code Playgroud)

这将导致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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

通过使用掩码,我只计算log(1)结果将乘以零的位置.现在log(0)只会发生在梯度下降的错误实现中.

开放式问题:如何使此解决方案更加干净?是否有可能以更清洁的方式实现类似的效果?

War*_*ser 2

如果您不介意使用 SciPy,您可以 importexpitxlog1pyfrom scipy.special

from scipy.special import expit, xlog1py
Run Code Online (Sandbox Code Playgroud)

并替换表达式

np.multiply(1 - y, np.log(1 - vec_sigmoid(X*theta.T)))
Run Code Online (Sandbox Code Playgroud)

xlog1py(1 - y, -expit(X*theta.T))
Run Code Online (Sandbox Code Playgroud)