首页 > 解决方案 > 如何在逻辑回归的 numpy 实现中避免 NaN?

问题描述

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

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

符号:

对于输入行向量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)。

我应该如何在矢量化实现中处理这个问题,因为是在(不仅是 where )np.multiply(1 - y, np.log(1 - vec_sigmoid(X*theta.T)))的每一行中计算的?Xy = 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)大的学习率)将设置为: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)只会在梯度下降的错误实现中发生。

开放式问题:我怎样才能使这个解决方案更干净?是否有可能以更清洁的方式达到类似的效果?

标签: python-3.xnumpymachine-learningnanlogistic-regression

解决方案


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

from scipy.special import expit, xlog1py

并替换表达式

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

xlog1py(1 - y, -expit(X*theta.T))

推荐阅读