使用scipy.optimize.fmin_bfgs进行逻辑回归分类器导致除零误差

时间:2015-12-01 05:22:44

标签: python numpy data-mining logistic-regression

我正在尝试实现我的二元逻辑回归分类器,我决定使用scipy.optimize.fmin_bfgs来最小化目标函数(loglikelihood),如下面的公式所示:

loglikelihood

此目标函数的梯度计算如下:

enter image description here

其中:

enter image description here

现在我的LogisticRegression类有sigmoid函数来计算sigmoid,loglikelihood函数来计算loglikelihood,梯度来计算梯度。最后,我有了我的learn_classifier方法,它调用optimize.fmin_bfgs函数来找到最佳权重向量。我的训练数据集由2013元组组成,每个元组有113个属性,其中第一个属性是结果(取一个或零)。这是我的代码:

from features_reader import FeaturesReader
import numpy as np
from scipy import optimize
from scipy.optimize import check_grad


class LogisticRegression:
    def __init__(self, features_reader = FeaturesReader()):
        features_reader.read_features()
        fHeight = len(features_reader.feature_data)
        fWidth = len(features_reader.feature_data[0])
        tHeight = len(features_reader.test_data)
        tWidth = len(features_reader.test_data[0])

        self.training_data = np.zeros((fHeight, fWidth))
        self.testing_data = np.zeros((tHeight, tWidth))

        print 'training data size: ', self.training_data.shape
        print 'testing data size: ', self.testing_data.shape
        for index, item in enumerate(features_reader.feature_data):
            self.training_data[index, 0] = item['outcome']
            self.training_data[index, 1:] = np.array([value for key, value in item.items() if key!='outcome'])

    def sigmoid(self, v_x, v_weight):
        return 1.0/(1.0 + np.exp(-np.dot(v_x, v_weight[1:])+v_weight[0]))

    def loglikelihood(self, v_weight, v_x, v_y):
        return -1*np.sum(v_y*np.log(self.sigmoid(v_x, v_weight)) + (1-v_y)*(np.log(1-self.sigmoid(v_x, v_weight))))

    def gradient(self, v_weight, v_x, v_y):
        gradient = np.zeros(v_weight.shape[0])
        for row, y in zip(v_x,v_y):
            new_row = np.ones(1+row.shape[0])
            new_row[1:] = row
            y_prime = self.sigmoid(new_row[1:], v_weight)
            gradient+=(y_prime-y)*new_row
        return gradient

    def learn_classifier(self):
        result = optimize.fmin_bfgs(f=self.loglikelihood,
                               x0=np.zeros(self.training_data.shape[1]),
                               fprime=self.gradient,
                               args=(self.training_data[:,1:], self.training_data[:,0]))
        return result


def main():
    features_reader = FeaturesReader(filename = 'features.csv', features_file = 'train_filter1.arff')
    logistic_regression = LogisticRegression(features_reader)

    result = logistic_regression.learn_classifier()
    print result


if __name__ == "__main__":
    main()

FeaturesReader类是读取我未在此处粘贴的csv文件的解析器。但我非常确定init函数正确地将csv解析为代表训练数据的2-D numpy数组。这个二维阵列具有形状(2013,113),其中第一列是训练输出。当我运行learn_classifier函数时,它会给出这些警告并终止:

training data size:  (2013, 113)
testing data size:  (4700, 113)

logistic_regression.py:26: RuntimeWarning: overflow encountered in exp
  return 1.0/(1.0 + np.exp(-np.dot(v_x, v_weight[1:])+v_weight[0]))

logistic_regression.py:30: RuntimeWarning: divide by zero encountered in log
  return -1*np.sum(v_y*np.log(self.sigmoid(v_x, v_weight)) + (1-v_y)*(np.log(1-self.sigmoid(v_x, v_weight))))

logistic_regression.py:30: RuntimeWarning: invalid value encountered in multiply
  return -1*np.sum(v_y*np.log(self.sigmoid(v_x, v_weight)) + (1-v_y)*(np.log(1-self.sigmoid(v_x, v_weight))))

Warning: Desired error not necessarily achieved due to precision loss.
     Current function value: nan
     Iterations: 1
     Function evaluations: 32
     Gradient evaluations: 32

所以我得到了这三个错误:1。除以零错误,2。在exp中遇到溢出3.在乘法中遇到无效值。并且算法在第一次迭代后终止,这是异常的。我不知道为什么会发生这些事情?你们认为我在计算对数似然和梯度方面做错了吗?你认为这些错误还来自哪里?更具体地说,在我的loglikelihood函数中,我的w_weight参数被假定为1D(形状= 113),我的v_x是2d并且具有形状(2013,112)(因为我不计算结果列),而我的v_y是1d并且有形状(2013)。

1 个答案:

答案 0 :(得分:0)

我自己一直在处理问题。对np.exp的调用可以非常快速地创建足够大的数字,使它们溢出float64。即使使用numpy的longdouble数据类型(may提供更高的分辨率,取决于你的CPU),我遇到了数值问题。

我发现至少在某些情况下,使用特征缩放和规范化解决了数值问题。如果您有n个要素,则每个要素减去该要素的平均值,然后除以要素的标准差。 numpy代码是:

for row in X.shape[0]:
    X[:, row] -= X[:, row].mean()
    X[:, row] /= X[:, row].std()

在没有显式循环的情况下,可能有更多的矢量化方法。 :)

您还可以查看我的toy implementation of it here

或者,您可以查看此技术,which describes taking the log of the exponential in order to prevent overflow