Logistic回归与梯度下降法
Logistic回歸為概率型非線性回歸模型,是研究二分類觀察結果與一些影響因素之間關系的一種
多變量分析方法。通常的問題是,研究某些因素條件下某個結果是否發生,比如醫學中根據病人的一些癥狀來判斷它是
否患有某種病。
?
在講解Logistic回歸理論之前,我們先從LR分類器說起。LR分類器,即Logistic Regression Classifier。
在分類情形下,經過學習后的LR分類器是一組權值,當測試樣本的數據輸入時,這組權值與測試數
據按照線性加和得到
?
???????????
?
這里是每個樣本的個特征。之后按照Sigmoid函數(又稱為Logistic函數)的形式求出
?
???????????
?
由于Sigmoid函數的定義域為,值域為,因此最基本的LR分類器適合對兩類目標進行分類。
所以Logistic回歸最關鍵的問題就是研究如何求得這組權值。此問題用極大似然估計來做。
?
?
下面正式地來講Logistic回歸模型。
?
考慮具有個獨立變量的向量,設條件慨率為根據觀測量相對于某事件發生
的概率。那么Logistic回歸模型可以表示為
?
???????????
?
其中,那么在條件下不發生的概率為
?
???????????
?
所以事件發生與不發生的概率之比為
?
???????????
?
這個比值稱為事件的發生比(the odds of experiencing an event),簡記為odds。
?
可以看出Logistic回歸都是圍繞一個Logistic函數來展開的。接下來就講如何用極大似然估計求分類器的參數。
?
假設有個觀測樣本,觀測值分別為,設為給定條件下得到的概率,
同樣地,的概率為,所以得到一個觀測值的概率為。
?
因為各個觀測樣本之間相互獨立,那么它們的聯合分布為各邊緣分布的乘積。得到似然函數為
?
???????????????????????????????????? ????
?
然后我們的目標是求出使這一似然函數的值最大的參數估計,最大似然估計就是求出參數,使
得取得最大值,對函數取對數得到
?
????????????
?
現在求向量,使得最大,其中。
?
這里介紹一種方法,叫做梯度下降法(求局部極小值),當然相對還有梯度上升法(求局部極大值)。
對上述的似然函數求偏導后得到
?
????????????
?
由于是求局部極大值,所以根據梯度上升法,有
?
????????????????????
?
根據上述公式,只需初始化向量全為零,或者隨機值,迭代到指定精度為止。
?
現在就來用C++編程實現Logistic回歸的梯度上升算法。首先要對訓練數據進行處理,假設訓練數據如下
?
訓練數據:TrainData.txt
1 0 0 1 0 1 0 0 1 2 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 1 2 1 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 0 0 1 0 0 0 1 0 2 0 0 0 1 0 1 0 0 2 1 0 2 0 0 0 1 0 2 0 1 0 0 0 0 0 1 0 1 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 1 2 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 2 0 0 1 0 0 0 1 0 2 0 1 0 0 0 2 0 1 1 1 0 1 0 1 1 0 0 1 0 1 2 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 0 1 0 1 0 1 2 1 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 1 1 1 0 1 0 1 0 1 0 2 0 1 2 0 0 0 0 1 2 1 0 0 0 1 0 1 0 2 0 1 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 2 1 0 0 1 1 0 0 0 0 1 0 0 1 0 2 1 0 0 0 0 2 1 0 0 0 0 1 1 0 2 0 0 1 1 0 0 0 1 0 1 0 0 0 0 2 1 0 0 1 0 0 1 0 0 1 0 2 1 0 2 1 0 2 1 0 2 1 0 1 1 0 2 1 0 0 1 0 0 0 1 2 1 1 0 1 0 2 1 0 1 1 0 1 1 0 0 0 1 2 1 0 0 0 0 1 1 0 0 1 0 1 1 0 0 0 0 2 1 0 1 1 0 1 1 0 0 1 0 1 0 1 1 0 1 2 1 0 1 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 2 1 0 1 0 1 2 0 1 1 0 0 1 1 0 2 0 1 2 1 0 2 0 0 0 1 0 1 0 0 1 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 2 1 0 2 0 1 1 1 0 0 0 1 0 1 0 0 0 0 0 1 0 2 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 2 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 2 1 0 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 1 0 2 0 0 0 1 0 1 0 0 0 1 0 2 0 0 1 1 0 0 0 1 2 1 0 1 0 1 2 0 0 0 0 1 2 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 1 0 0 0 1 0 2 0 0 1 1 0 1 0 0 1 1 0 1 0 1 0 0 0 1 1 0 1 1 0 2 1 0 0 1 0 0 1 0 0 0 0 1 1 0 1 0 1 1 1 0 2 1 0 0 1 0 0 0 0 1 1 0 2 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 0 2 1 0 1 0 0 2 1 0 2 1 1 1 1 0 0 1 0 0 1 0 0 1 0 0 1 0 2 1 0 0 0 1 1 1 0 2 1 0 1 1 0 0 1 0 1 1 1 0 0 0 2 1 0 2 1 0 2 1 1 1 0 0 0 1 0 0 1 0 1 1 0 2 1 0 0 1 0 0 1 0 1 1 0 1 1 0 0 1 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 1 1 0 2 1 0 1 1 0 0 0 0 0 1 1 2 0 0 2 1 0 0 1 0 2 0 1 0 0 1 0 0 1 0 1 0 1 0 1 0 0 0 0 0 1 2 1 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 1 2 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 1 2 1 0 2 0 1 2 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 2 0 1 1 1 0 0 0 1 0 0 1 1 0 1 0 0 0 1 0 1 1 1 0 1 0 1 1 0 0 0 0 1 0 0 0 1 0 1 1 1 0 1 0 1 2 0 0 2 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 2 0 1 1 0 0 0 0 1 2 0 0 1 0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 2 0 1 2 1 0 1 0 0 1 0 0 0 0 1 0 0 0 2 0 0 1 1 1 0 0 1 0 0 0 0 0 1 0 1 0 2 0 1 0 1 0 0 0 1 0 1 0 2 0 0 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 1 2 0 0 0 0 0 2 0 0 1 1 0 0 0 1 0 1 0 0 0 0 0 1 0 2 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 1 2 0 0 2 0 1 0 1 1 0 0 1 0 1 0 0 0 1 2 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 2 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 0 1 1 0 2 0 0 0 1 0 1 0 0 0 0 0 2 0 0 2 1 0 0 0 1 2 1 0 1 0 1 0 0 1 0 0 1 2 1 0 0 0 1 2 1 0 0 0 1 0 1 0 1 0 1 2 1 0 0 0 0 2 0 0 1 0 0 0 0 0 0 0 0 2 1 0 0 0 1 0 1 0 2 0 0 0 1 0 1 0 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 0 0 1 0 1 0 1 1 0 2 1 0 1 1 0 1 0 0 2 1 0 2 1 0 1 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 0 1 0 1 1 1 0 0 0 1 1 1 0 1 0 0 1 0 0 1 0 0 1 1 0 0 1 1 1 1 1 1 0 1 1 0 2 1 0 0 1 0 2 0 0 1 1 0 2 1 0 0 0 1 2 1 0 2 1 1 1 1 0 0 1 0 0 1 0 0 0 1 0 1 0 2 1 0 1 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 0 2 1 0 0 1 0 2 1 0 0 0 0 1 1 0 0 1 0 2 1 0 1 1 0上面訓練數據中,每一行代表一組訓練數據,每組有7個數組,第1個數字代表ID,可以忽略之,2~6代表這組訓
練數據的特征輸入,第7個數字代表輸出,為0或者1。每個數據之間用一個空格隔開。
?
首先我們來研究如何一行一行讀取文本,在C++中,讀取文本的一行用getline()函數。
getline()函數表示讀取文本的一行,返回的是讀取的字節數,如果讀取失敗則返回-1。用法如下:
#include <iostream> #include <string.h> #include <fstream> #include <string> #include <stdio.h>using namespace std;int main() {string filename = "data.in";ifstream file(filename.c_str());char s[1024];if(file.is_open()){while(file.getline(s,1024)){int x,y,z;sscanf(s,"%d %d %d",&x,&y,&z);cout<<x<<" "<<y<<" "<<z<<endl;}}return 0; }
拿到每一行后,可以把它們提取出來,進行系統輸入。?Logistic回歸的梯度上升算法實現如下
?
代碼:
#include <iostream> #include <string.h> #include <fstream> #include <stdio.h> #include <math.h> #include <vector>#define Type double #define Vector vector using namespace std;struct Data {Vector<Type> x;Type y; };void PreProcessData(Vector<Data>& data, string path) {string filename = path;ifstream file(filename.c_str());char s[1024];if(file.is_open()){while(file.getline(s, 1024)){Data tmp;Type x1, x2, x3, x4, x5, x6, x7;sscanf(s,"%lf %lf %lf %lf %lf %lf %lf", &x1, &x2, &x3, &x4, &x5, &x6, &x7);tmp.x.push_back(1);tmp.x.push_back(x2);tmp.x.push_back(x3);tmp.x.push_back(x4);tmp.x.push_back(x5);tmp.x.push_back(x6);tmp.y = x7;data.push_back(tmp);}} }void Init(Vector<Data> &data, Vector<Type> &w) {w.clear();data.clear();PreProcessData(data, "TrainData.txt");for(int i = 0; i < data[0].x.size(); i++)w.push_back(0); }Type WX(const Data& data, const Vector<Type>& w) {Type ans = 0;for(int i = 0; i < w.size(); i++)ans += w[i] * data.x[i];return ans; }Type Sigmoid(const Data& data, const Vector<Type>& w) {Type x = WX(data, w);Type ans = exp(x) / (1 + exp(x));return ans; }Type Lw(const Vector<Data>& data, Vector<Type> w) {Type ans = 0;for(int i = 0; i < data.size(); i++){Type x = WX(data[i], w);ans += data[i].y * x - log(1 + exp(x));}return ans; }void Gradient(const Vector<Data>& data, Vector<Type> &w, Type alpha) {for(int i = 0; i < w.size(); i++){Type tmp = 0;for(int j = 0; j < data.size(); j++)tmp += alpha * data[j].x[i] * (data[j].y - Sigmoid(data[j], w));w[i] += tmp;} }void Display(int cnt, Type objLw, Type newLw, Vector<Type> w) {cout<<"第"<<cnt<<"次迭代: ojLw = "<<objLw<<" 兩次迭代的目標差為: "<<(newLw - objLw)<<endl;cout<<"參數w為: ";for(int i = 0; i < w.size(); i++)cout<<w[i]<<" ";cout<<endl;cout<<endl; }void Logistic(const Vector<Data>& data, Vector<Type> &w) {int cnt = 0;Type alpha = 0.1;Type delta = 0.00001;Type objLw = Lw(data, w);Gradient(data, w, alpha);Type newLw = Lw(data, w);while(fabs(newLw - objLw) > delta){objLw = newLw;Gradient(data, w, alpha);newLw = Lw(data, w);cnt++;Display(cnt,objLw,newLw, w);} }void Separator(Vector<Type> w) {Vector<Data> data;PreProcessData(data, "TestData.txt");cout<<"預測分類結果:"<<endl;for(int i = 0; i < data.size(); i++){Type p0 = 0;Type p1 = 0;Type x = WX(data[i], w);p1 = exp(x) / (1 + exp(x));p0 = 1 - p1;cout<<"實例: ";for(int j = 0; j < data[i].x.size(); j++)cout<<data[i].x[j]<<" ";cout<<"所屬類別為:";if(p1 >= p0) cout<<1<<endl;else cout<<0<<endl;} }int main() {Vector<Type> w;Vector<Data> data;Init(data, w);Logistic(data, w);Separator(w);return 0; }?
測試數據:TestData.txt
10009 1 0 0 1 0 1 10025 0 0 1 2 0 0 20035 0 0 1 0 0 1 20053 1 0 0 0 0 0 30627 1 0 1 2 0 0 30648 2 0 0 0 1 0?
總結
以上是生活随笔為你收集整理的Logistic回归与梯度下降法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Go语言的分词器(sego)
- 下一篇: Logistic回归与牛顿迭代法