1. ロジスティック回帰モデル
ロジスティック回帰モデルは実際には分類モデルであり、ここでの実装は Li Hang 著の「Statistical Machine Learning」と Zhou Zhihua 著の「Machine Learning」の 2 冊の教科書に基づいています。
入力がxxであるとします。×、××x は多次元にすることができ、xxxでyyを予測するy,y ∈ { 0 , 1 } y\in \{0,1\}y∈{ 0 ,1 } . ロジスティックモデルは次のとおりです。
p ( Y = 1 ∣ x ) = exp ( w ⋅ x ) 1 + exp ( w ⋅ x ) (1) p(Y=1|x)=\frac{exp(w\cdot x)}{1+exp (w\cdot x)}\tag{1}p ( Y=1∣ x )=1+e x p ( w⋅× )e x p ( w⋅×)( 1 )
ここでパラメータwwwは学習したいものです。注: これには重み係数とバイアス (バイアス) b が含まれます。プログラムを作成する場合は、この表現の方が簡潔です。
第二に、最尤法パラメータ推定
パラメータwwwは学習する必要があるもので、最尤法を使用してモデル パラメーターを推定します。
設定:
P ( Y = 1 ∣ x ) = π ( x ) , P ( Y = 0 ∣ x ) = 1 − π ( x ) (2) P(Y=1|x)=\pi(x),\quad P (Y=0|x)=1-\pi(x)\tag{2}P ( Y)=1∣ x )=π ( x ) 、P ( Y)=0∣ x )=1−π ( x )( 2 )
尤度関数は次のとおりです。
∏ i = 1 N [ π ( xi ) ] yi [ 1 − π ( xi ) ] 1 − yi (3) \prod_{i=1}^N[\pi(x_i)]^{y_i}[1-\ pi(x_i)]^{1-y_i} \tag{3}i = 1∏N[ p ( x私は) ]y私は[ 1−π ( x私は) ]1 − y私は( 3 )
この指数形式は導出に役立たないため、次のように対数形式に変換する必要があります。
L ( w ) = ∑ i = 1 N [ yilog π ( xi ) + ( 1 − yi ) log ( 1 − π ( xi ) ) ] = ∑ i = 1 N [ yilog ( π ( xi ) 1 − π ( xi ) ) ) + log ( 1 − π ( xi ) ) ] = ∑ i = 1 N [ yi ( w ⋅ xi ) − log ( 1 + exp ( w ⋅ xi ) ) ] (4) \begin{aligned} L(w )=&\sum_{i=1}^N[y_ilog\pi(x_i)+(1-y_i)log(1-\pi(x_i))] \\ =&\sum_{i=1}^N [ y_ilog(\frac{\pi(x_i)}{1-\pi(x_i)})+log(1-\pi(x_i))]\\ =&\sum_{i=1}^{N}[y_i (w\cdot x_i)-log(1+exp(w\cdot x_i))] \end{aligned} \tag{4}L ( w )===i = 1∑N[ y私はl o g π ( x私は)+( 1−y私は) l o g ( 1−π ( x私は))]i = 1∑N[ y私はl o g (1−π ( x私は)π ( x私は))+l o g ( 1−π ( x私は))]i = 1∑N[ y私は( w⋅バツ私は)−l o g ( 1+e x p ( w⋅バツ私は)) ]( 4 )
L ( w )の場合L(w)L ( w )の最大値を見つけてwwwの推定値
第三に、尤度関数を解くための勾配降下法
勾配降下法は最小値を見つけることであり、取得したいのはL ( w ) L(w)です。L ( w )の最大値、したがって、L ( w ) L(w)L ( w )の反対の数、つまり:
arg min w − L ( w ) (5) \argmin_{w}-L(w) \tag{5}w引数分− L ( w )( 5 )
L ( w )の場合L(w)L(w)关于 w w wの微分は
( − L ( w ) ) ′ = − ∑ i = 1 N [ ( yi ⋅ xi ) − exp ( w ⋅ xi ) 1 + exp ( w ⋅ x ) ⋅ xi ] = − ∑ i = 1 N [ ( yi − exp ( w ⋅ xi ) 1 + exp ( w ⋅ x ) ) ⋅ xi ] = ∑ i = 1 N [ ( exp ( w ⋅ xi ) 1 + exp ( w ⋅ x ) − yi ) ⋅ xi ] (6) \ begin{aligned} (-L(w))'=&-\sum_{i=1}^N[(y_i\cdot x_i)-\frac{exp(w\cdot x_i)}{1+exp(w\ cdot x)}\cdot x_i]\\ =&-\sum_{i=1}^N[(y_i-\frac{exp(w\cdot x_i)}{1+exp(w\cdot x)})\ cdot x_i]\\ =&\sum_{i=1}^N[(\frac{exp(w\cdot x_i)}{1+exp(w\cdot x)}-y_i)\cdot x_i] \end{整列} \tag{6}( − L ( w ) )』===−i = 1∑N[( y私は⋅バツ私は)−1+e x p ( w⋅× )e x p ( w⋅バツ私は)⋅バツ私は]−i = 1∑N[( y私は−1+e x p ( w⋅× )e x p ( w⋅バツ私は))⋅バツ私は]i = 1∑N[(1+e x p ( w⋅× )e x p ( w⋅バツ私は)−y私は)⋅バツ私は]( 6 )
次にパラメータを取得しますwwwの更新式は次のとおりです。
w ′ = w − lr ⋅ ( − L ( w ) ′ ) = w − lr ⋅ ( ∑ i = 1 N [ ( exp ( w ⋅ xi ) 1 + exp ( w ⋅ x ) − yi ) ⋅ xi ] ) ( 7) \begin{aligned} w'=&w-lr\cdot(-L(w)')\\ =&w-lr\cdot(\sum_{i=1}^N[(\frac{exp(w\ cdot x_i)}{1+exp(w\cdot x)}-y_i)\cdot x_i]) \end{aligned} \tag{7}w』==w−l r⋅( − L ( w )) _w−l r⋅(i = 1∑N[(1+e x p ( w⋅× )e x p ( w⋅バツ私は)−y私は)⋅バツ私は] )( 7 )
最適化手法の選択については、当初はスイカ本に掲載されているニュートン法を選択しましたが、ニュートン法の利点はより速い収束速度が得られることですが、欠点はヘッセ行列が特異行列が表示されます。解決できないケースが表示されます。
したがって、準ニュートン法を最適化に使用することができ、この問題を解決しながらも高速に収束することができます。
ただし、準ニュートン法には詳しくありません。また、勾配降下法は収束が遅いかもしれませんが、実装は比較的簡単であるため、ここでは尤度関数の最適化に勾配降下法を使用します。
4 番目、weka ベースのコード実装
package weka.classifiers.myf;
import weka.classifiers.Classifier;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.matrix.Matrix;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.NominalToBinary;
import weka.filters.unsupervised.attribute.Standardize;
import java.util.Arrays;
/**
* @author YFMan
* @Description 自定义的 Logistic 回归分类器
* @Date 2023/6/13 11:02
*/
public class myLogistic extends Classifier {
// 用于存储 线性回归 系数 的数组
private double[] m_Coefficients;
// 类别索引
private int m_ClassIndex;
// 牛顿法的迭代次数
private int m_MaxIterations = 1000;
// 属性数量
private int m_numAttributes;
// 系数数量
private int m_numCoefficients;
// 梯度下降步长
private double m_lr = 1e-4;
// 标准化数据的过滤器
public static final int FILTER_STANDARDIZE = 1;
// 用于标准化数据的过滤器
protected Filter m_StandardizeFilter = null;
// 用于将 normal 转为 binary 的过滤器
protected Filter m_NormalToBinaryFilter = null;
/*
* @Author YFMan
* @Description 采用牛顿法来训练 logistic 回归模型
* @Date 2023/5/9 22:08
* @Param [data] 训练数据
* @return void
**/
public void buildClassifier(Instances data) throws Exception {
// 设置类别索引
m_ClassIndex = data.classIndex();
// 设置属性数量
m_numAttributes = data.numAttributes();
// 系数数量 = 输入属性数量 + 1(截距参数b)
m_numCoefficients = m_numAttributes;
// 初始化 系数数组
m_Coefficients = new double[m_numCoefficients];
Arrays.fill(m_Coefficients, 0);
// 将输入数据进行标准化
m_StandardizeFilter = new Standardize();
m_StandardizeFilter.setInputFormat(data);
data = Filter.useFilter(data, m_StandardizeFilter);
// 将类别属性转为二值属性
m_NormalToBinaryFilter = new NominalToBinary();
m_NormalToBinaryFilter.setInputFormat(data);
data = Filter.useFilter(data, m_NormalToBinaryFilter);
// 梯度下降法
for(int curPerformIteration = 0; curPerformIteration < m_MaxIterations;curPerformIteration++){
double[] deltaM_Coefficients = new double[m_numCoefficients];
// 计算 l(w) 的一阶导数
for(int i = 0;i<data.numInstances();i++){
double yi = data.instance(i).value(m_ClassIndex);
double wxi = 0;
int column = 0;
for(int j=0;j<m_numAttributes;j++){
if(j!=m_ClassIndex){
wxi += m_Coefficients[column] * data.instance(i).value(j);
column++;
}
}
// 加上截距参数 b
wxi += m_Coefficients[column];
double pi1 = Math.exp(wxi) / (1 + Math.exp(wxi));
for(int k=0;k<m_numCoefficients - 1;k++){
deltaM_Coefficients[k] += m_lr * (pi1 - yi) * data.instance(i).value(k);
}
// 这里计算 bias b 对应的更新量
deltaM_Coefficients[m_numCoefficients - 1] += m_lr * (pi1 - yi);
}
// 进行参数更新
for(int k=0;k<m_numCoefficients;k++){
m_Coefficients[k] -= deltaM_Coefficients[k];
}
// 如果参数更新量小于阈值,则停止迭代
double delta = 0;
for(int k=0;k<m_numCoefficients;k++){
delta += deltaM_Coefficients[k] * deltaM_Coefficients[k];
}
if(delta < 1e-6){
break;
}
}
}
/*
* @Author YFMan
* @Description // 分类实例
* @Date 2023/6/16 11:17
* @Param [instance]
* @return double[]
**/
public double[] distributionForInstance(Instance instance) throws Exception {
// 将输入数据进行标准化
m_StandardizeFilter.input(instance);
instance = m_StandardizeFilter.output();
// 将输入属性二值化
m_NormalToBinaryFilter.input(instance);
instance = m_NormalToBinaryFilter.output();
double[] result = new double[2];
result[0] = 0;
result[1] = 0;
int column = 0;
for(int i=0;i<m_numAttributes;i++){
if(m_ClassIndex != i){
result[0] += instance.value(i) * m_Coefficients[column];
column++;
}
}
result[0] += m_Coefficients[column];
result[0] = 1 / (1 + Math.exp(result[0]));
result[1] = 1 - result[0];
return result;
}
/*
* @Author YFMan
* @Description 主函数 生成一个线性回归函数预测器
* @Date 2023/5/9 22:35
* @Param [argv]
* @return void
**/
public static void main(String[] argv) {
runClassifier(new myLogistic(), argv);
}
}