我正在尝试实现我的二元逻辑回归分类器,我决定使用scipy.optimize.fmin_bfgs来最小化目标函数(loglikelihood),如下面的公式所示:
此目标函数的梯度计算如下:
其中:
现在我的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)。
答案 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。