線性迴歸(Linear Regression)

基本概念

線性迴歸(Linear Regression )是利用稱爲線性迴歸方程的最小平方函數對一個或多個自變量和因變量之間關係進行建模的一種迴歸分析。這種函數是一個或多個稱爲迴歸係數的模型參數的線性組合。只有一個自變量的情況稱爲簡單迴歸,大於一個自變量情況的叫做多元迴歸。

假設函數(Hypothesis function)

公式

代數表示

hθ(x)=θ0+θ1x1+...+θnxn

向量表示
hθ(x)=θτX

注:θn+1 維的列向量X(n+1)m 維的向量。

代價函數(Cost function)

公式

結合平方誤差和公式

J(θ)=1mΣmi=1(x(i)y(i))2

選擇該公式的原因:

假設根據特徵的預測結果與實際結果有誤差ϵ(i) ,那麼預測結果θτx(i) 與真實結果y(i) 滿足:

y(i)=θτx(i)+ϵ(i)

一般來講,誤差滿足平均值爲0的高斯分佈,也就是正態分佈。那麼x和y的條件概率也就是:
p(y(i)|x(i);θ)=12πσexp((y(i)θτx(i))22σ2)

這樣就估計了一條樣本的結果概率,然而我們期待的是模型能夠在全部樣本上預測最準,也就是概率積最大。注意這裏的概率積是概率密度函數積,連續函數的概率密度函數與離散值的概率函數不同。這個概率積成爲最大似然估計。我們希望在最大似然估計得到最大值時確定θ。那麼需要對最大似然估計公式求導,求導結果既是:
12Σmi=1(θτx(i)y(i))2

這就解釋了爲何誤差函數要使用平方和。當然推導過程中也做了一些假定,但這個假定符合客觀規律。

代數表示

J(θ)=12mΣmi=1(hθ(x(i))y(i))2

向量表示
J(θ)=12mθτXY

注:θn+1 維的列向量,X(n+1)m 維的向量,Ym+1 維的列向量。

目標

通過訓練找到使得J(θ) 值最小的θ .我們可以用梯度下降(gradient descent )法來求。

梯度下降法:

基本概念

梯度下降法,就是利用負梯度方向來決定每次迭代的新的搜索方向,使得每次迭代能使待優化的目標函數逐步減小。

在線性迴歸中的應用

每次對代價函數求偏導,得到前進的方向,然後設定一個學習的速率α ,使得代價函數的值往小的方向收斂。
代數公式:

θi=θiαyJ(θ)

結合線性迴歸:
θj=θjαΣmi=1(hθ(x(i))y(i))x(i)j

注:α 的選擇十分關鍵,過小需要的迭代次數較長,過大可能不會收斂,所有的θ 需要同時更新(批量梯度下降)
向量表示:
θ=θαmX(XθY)

注:這樣也保證了同時更新。
可以用線性代數的方法直接求出θ
θ=(XτX)1XτY

matalb Code

Cost function

function J = computeCostMulti(X, y, theta)

m = length(y); % number of training examples

J = 0;

predictions = X*theta; %假設函數的值

sqrterror = (predictions-y).^2; %平方誤差

J=1/(2*m)*sum(sqrterror);

end

gradient descent

function [theta, J_history] = gradientDescentMulti(X, y, theta, alpha, num_iters)

m = length(y); % number of training examples
J_history = zeros(num_iters, 1);

for iter = 1:num_iters

    theta = theta - alpha/m*(X'*(X*theta-y));

    J_history(iter) = computeCostMulti(X, y, theta);

end

end

實例

鏈接

房價預測

Code

#include <bits/stdc++.h>

using namespace std;

typedef vector<double > vd;

struct data{
    vd x;
    double y;
    data(){
        x.clear();
        y=0;
    }
    data(const vd &_x,const double &_y){
        x=_x;
        y=_y;
    }
};

double H(vd x,vd thea)//hypothesis function
{
    int len = thea.size();
    double sum = 0;
    for(int i=0;i<len;i++)
        sum=sum+x[i]*thea[i];
    return sum;
}

double getvalue(data dt[],vd thea,int n){//最小二乘得到方差
    double sum = 0;
    for(int i=0;i<n;i++){
        double tmpsum = H(dt[i].x,thea);
        sum+=(tmpsum-dt[i].y)*(tmpsum-dt[i].y);
    }
    return sum/2.0;
}

void gradient(data dt[],vd &thea,int n,double alpha)//梯度下降更新thea
{
    int len = thea.size();
    vd tmp;
    for(int i=0;i<len;i++){
        double tmpsum = 0;
        for(int j=0;j<n;j++){
            tmpsum = tmpsum + alpha*(H(dt[j].x,thea)-dt[j].y)*dt[j].x[i];
        }
        tmp.push_back(thea[i]-tmpsum);
    }
    thea = tmp;
}

void solve(data dt[],vd &thea,int n)//處理迭代
{
    double alpha = 0.005;
    double delta = 1e-6;
    double pre = getvalue(dt,thea,n);
    double now = 0;
    while(abs(pre-now)>delta){
        pre = now;
        now = getvalue(dt,thea,n);
        gradient(dt,thea,n,alpha);
    }
}

int main()
{
    int f,t,m,n;
    while(~scanf("%d%d",&f,&n)){
        data test[110];
        for(int i=0;i<n;i++){
            vd tmp;
            double x;
            tmp.push_back(1.0);
            for(int i=0;i<f;i++){
                scanf("%lf",&x);
                tmp.push_back(x);
            }
            scanf("%lf",&x);
            test[i]=data(tmp,x);
        }
        scanf("%d",&m);
        data ans[110];
        for(int i=0;i<m;i++){
            vd tmp;
            double x;
            tmp.push_back(1.0);
            for(int i=0;i<f;i++){
                scanf("%lf",&x);
                tmp.push_back(x);
            }
            ans[i]=data(tmp,0);
        }
        vd thea;
        for(int i=0;i<=f;i++)
            thea.push_back(0);
        solve(test,thea,n);
        for(int i=0;i<m;i++){
            ans[i].y=H(ans[i].x,thea);
            printf("%.2lf\n",ans[i].y);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章