总结与问题
我正在使用liblinear 2.30 – 我注意到生产环境中存在类似的问题,因此我尝试通过简化的训练来隔离它,包含2个类,每个类一个训练文档,词汇表中5个特征的权重相同,以及一个仅包含一个特征的简单测试文档,该特征仅存在于类2中。
-
a) 特征值用于什么目的?
-
b) 我想了解为什么这个只包含一个特征的测试文档,该特征仅存在于一个类中,却未被强烈预测为该类?
-
c) 我并不期望每个特征有不同的值。将每个特征值从1增加到其他值是否有其他影响?我如何确定这个数字?
-
d) 我的更改是否会对其他更复杂的训练产生不良影响?
我尝试过的方法
下面你会找到与简单训练相关的数据(请关注特征5):
> cat train.txt1 1:1 2:1 3:12 2:1 4:1 5:1> train -s 0 -c 1 -p 0.1 -e 0.01 -B 0 train.txt model.biniter 1 act 3.353e-01 pre 3.333e-01 delta 6.715e-01 f 1.386e+00 |g| 1.000e+00 CG 1iter 2 act 4.825e-05 pre 4.824e-05 delta 6.715e-01 f 1.051e+00 |g| 1.182e-02 CG 1> cat model.binsolver_type L2R_LRnr_class 2label 1 2nr_feature 5bias 0w0.337414143653901600.3374141436539016-0.3374141436539016-0.33741414365390160
这是模型的输出:
solver_type L2R_LRnr_class 2label 1 2nr_feature 5bias 0w0.337414143653901600.3374141436539016-0.3374141436539016-0.337414143653901601 5:10
下面是我的模型预测结果:
> cat test.txt1 5:1> predict -b 1 test.txt model.bin test.outAccuracy = 0% (0/1)> cat test.outlabels 1 22 0.416438 0.583562
这里我有点惊讶,因为预测结果仅为[0.42, 0.58]
,而特征5仅存在于类2中。为什么呢?因此,我尝试将测试文档的特征值从1增加到10:
> cat newtest.txt1 5:10> predict -b 1 newtest.txt model.bin newtest.outAccuracy = 0% (0/1)> cat newtest.outlabels 1 22 0.0331135 0.966887
现在我得到了更好的预测结果[0.03, 0.97]
。因此,我尝试重新编译我的训练数据,将所有特征设置为10:
> cat newtrain.txt1 1:10 2:10 3:102 2:10 4:10 5:10> train -s 0 -c 1 -p 0.1 -e 0.01 -B 0 newtrain.txt newmodel.biniter 1 act 1.104e+00 pre 9.804e-01 delta 2.508e-01 f 1.386e+00 |g| 1.000e+01 CG 1iter 2 act 1.381e-01 pre 1.140e-01 delta 2.508e-01 f 2.826e-01 |g| 2.272e+00 CG 1iter 3 act 2.627e-02 pre 2.269e-02 delta 2.508e-01 f 1.445e-01 |g| 6.847e-01 CG 1iter 4 act 2.121e-03 pre 1.994e-03 delta 2.508e-01 f 1.183e-01 |g| 1.553e-01 CG 1> cat newmodel.binsolver_type L2R_LRnr_class 2label 1 2nr_feature 5bias 0w0.1942051039536484600.19420510395364846-0.19420510395364846-0.194205103953648460> predict -b 1 newtest.txt newmodel.bin newtest.outAccuracy = 0% (0/1)> cat newtest.outlabels 1 22 0.125423 0.874577
再次预测结果对于类2仍然可以接受:0.87
回答:
a) 特征值用于什么目的?
每个n个特征的实例被视为n维空间中的一个点,并附有一个给定的标签,比如+1或-1(在你的例子中是1或2)。线性SVM试图找到最佳超平面,将这些实例分成两个集合,比如SetA和SetB。当SetA包含更多标记为+1的实例,而SetB包含更多标记为-1的实例时,超平面被认为比其他超平面更好,即更准确。最佳超平面被保存为模型。在你的情况下,超平面的公式为:
f(x)=w^T x
其中w是模型,例如在你的第一个案例中是(0.33741,0,0.33741,-0.33741,-0.33741)。
概率(对于LR)的公式为:
prob(x)=1/(1+exp(-y*f(x))
其中y=+1或-1。参见LIBLINEAR论文的附录L。
b) 我想了解为什么这个只包含一个特征的测试文档,该特征仅存在于一个类中,却未被强烈预测为该类?
不仅1 5:1
给出了如[0.42,0.58]
的弱概率,如果你预测2 2:1 4:1 5:1
,你会得到[0.337417,0.662583]
,这似乎表明求解器对结果也不是很自信,即使输入与训练数据集完全相同。
根本原因在于f(x)的值,或者可以简单地看作x与超平面之间的距离。只有当距离无限大时(见prob(x)),才能100%确信x属于某个类别。
c) 我并不期望每个特征有不同的值。将每个特征值从1增加到其他值是否有其他影响?我如何确定这个数字?
TL;DR
放大训练集和测试集就像拥有更大的惩罚参数C(-c选项)。因为更大的C意味着对错误有更严格的惩罚,直观上说,求解器对预测更有信心。
放大训练集的每个特征就像拥有更小的C。具体来说,逻辑回归解决以下关于w的方程。
min 0.5 w^T w + C ∑i log(1+exp(−yi w^T xi))
(LIBLINEAR论文的eq(3))
对于大多数实例,yi w^T xi
是正的,且更大的xi
意味着更小的∑i log(1+exp(−yi w^T xi))
。因此效果有些类似于拥有更小的C,而更小的C意味着更小的|w|。
另一方面,放大测试集相当于拥有更大的|w|。因此,放大训练集和测试集的基本效果是
(1). 训练时拥有更小的|w|(2). 然后,测试时拥有更大的|w|
因为(2)的效果比(1)更显著,总体来说,放大训练集和测试集就像拥有更大的|w|,或者说拥有更大的C。
我们可以在数据集上运行,并将每个特征乘以10^12。在C=1的情况下,我们有模型和概率
> cat model.bin.m1e12.c1solver_type L2R_LRnr_class 2label 1 2nr_feature 5bias 0w3.0998430106024949e-12 0 3.0998430106024949e-12 -3.0998430106024949e-12 -3.0998430106024949e-12 0 > cat test.out.m1e12.c1labels 1 22 0.0431137 0.956886
接下来我们在原始数据集上运行。在C=10^12的情况下,我们有概率
> cat model.bin.m1.c1e12solver_type L2R_LRnr_class 2label 1 2nr_feature 5bias 0w3.0998430101989314 0 3.0998430101989314 -3.0998430101989314 -3.0998430101989314 0 > cat test.out.m1.c1e12labels 1 22 0.0431137 0.956886
因此,因为更大的C意味着对错误有更严格的惩罚,所以直观上求解器对预测更有信心。
d) 我的更改是否会对其他更复杂的训练产生不良影响?
从(c)我们知道你的更改就像拥有更大的C,这将导致更好的训练准确性。但几乎可以肯定,当C变得太大时,模型会过拟合训练集。结果,模型无法承受训练集中的噪声,测试准确性会表现得很差。
至于找到一个好的C,一个流行方法是通过交叉验证(-v选项)。
最后,
这可能有点跑题,但你可能想看看如何预处理文本数据。通常(例如,liblinear的作者在这里建议),对数据进行实例级归一化是很常见的做法。
对于文档分类,我们的经验表明,如果你将每个文档归一化为单位长度,那么不仅训练时间更短,而且性能也更好。