机器学习

1.KNN

一个小栗子

import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
train_x=np.arange(1,32,2).reshape(-1,2)
train_y=np.array([0,0,0,0,1,1,1,1])
predict_x=np.array([23.6,13.6])
KNN_clf=KNeighborsClassifier(n_neighbors=4)#设置K值
KNN_clf.fit(train_x,train_y)#训练模型,即拟合
predict_y=KNN_clf.predict(predict_x.reshape(1,-1))#输入测试集
print(predict_y)
plt.scatter(train_x[train_y==0,0],train_x[train_y==0,1],color="r")
plt.scatter(train_x[train_y==1,0],train_x[train_y==1,1],color="b")
plt.scatter(predict_x[0],predict_x[1],color="g")
plt.show()

实现sklearn中的knn

class KNNClassifer:
    def __init__(self,k):
        assert k>=1,"k must be valid"
        self.k=k
        self._x_train=None
        self._y_train=None
    
    def fit(self,x_train,y_train):
        #根据训练测试集,训练KNN分类器
        assert x_train.shape[0]==y_train.shape[0],\
            "the size of x_train must be equal to the size of y_train"
        assert self.k<=x_train.shape[0],\
            "the size of x_train must be at least k"
        
        self._x_train=x_train
        self._y_train=y_train
        return self
    
    def predict(self,x_predict):
        assert self._x_train is not None and self._y_train is not None,\
            "must fit before predict"
        assert x_predict.shape[1]==self._x_train.shape[1],\
            "the feature number of x_predict must be equal to x_train"
        y_predict=[self._predict(x) for x in x_predict]
        return np.array(y_predict)
    
    def _predict(self,x):
        assert x.shape[0]==self._x_train.shape[1],\
            "the feature number of x must be equal to x_train"
        distances=[sqrt(np.sum(one-x)**2) for one in self._x_train]
        nearest=np.argsort(distances)
        topK_y=[self._y_train[i] for i in nearest]
        votes=Counter(topK_y)
        return votes.most_common(1)[0][0]
        

将数据分为测试数据和训练数据

def train_test_split(x,y,test_ratio=0.2,seed=None):
    assert x.shape[0]==y.shape[0],\
        "the size of x should be equal to the size of y"
    assert 0.0<=test_ratio<=1.0,\
        "test ratio must be valid"
    if seed:
        np.random.seed(seed)
        
    shuffle_index=np.random.permutation(len(x))
    test_size=int(test_ratio*len(x))
    test_index=shuffle_index[:test_size]
    train_index=shuffle_index[test_size:]
    
    X_train=x[train_index]
    Y_train=y[train_index]
    X_test=x[test_index]
    Y_test=y[test_index]
    
    return X_train,X_test,Y_train,Y_test

iris的分类:

from sklearn.neighbors import KNeighborsClassifier
knn_clf=KNeighborsClassifier(n_neighbors=3)
knn_clf.fit(X_train,Y_train)
Y_predict=knn_clf.predict(X_test)
knn_clf.score(X_test,Y_test)

数据归一化:
要将所有数据映射到同一尺度,这样有利于计算数据间的距离
最简单的方法:
1.最值归一化(normalization),将所有的数据映射到0~1之间
X_{scale}=\frac{X-X_{min}}{X_{max}-X_{min}}
此种归一化方法适用于有明显边界的特征:例如考试分数,其边界为0~100

y=np.array(np.random.randint(0,100,size=(50,5)),dtype=float)
for i in range(len(y[0])):
    y[:,i]=((y[:,i]-np.min(y[:,i]))/(np.max(y[:,i])-np.min(y[:,i])))

2.均值方差归一化(standardization):将所有数据归一到均值为0,方差为1的分布中
适用于没有明显分界的数据分布;但是可能有极端数据
X_{scale}=\frac{X-X_{mean}}{S}

z=np.array(np.random.randint(0,100,size=(50,5)),dtype=float)
for i in range(len(z[0])):
    z[:,i]=(z[:,i]-np.mean(z[:,i]))/np.std(z[:,i])

众所周知,我们的数据集分为训练集和测试集,对于测试集的均值方差归一化,不能用测试集的均值和方差,而要用训练集的均值和方差,因为真实数据中很难得到其均值和方差。

sklearn中也封装了scaler类来进行数据归一化

2.回归

sklearn中的回归类的实现:

import numpy as np
import matplotlib.pyplot as plt
class SimpleLinearRegression1:
    
    def __init__(self):
        #初始化斜率a和截距b
        self.a_=None
        self.b_=None
        
    def fit(self,x_train,y_train):
        #根据训练数据集训练模型,即求出回归方程的a和b
        assert x_train.ndim==1,\
        "简单线性回归只能解决特征数为1的问题"
        assert len(x_train)==len(y_train),\
        "x_train与y_train的长度必须相同"
        
        x_mean=np.mean(x_train)
        y_mean=np.mean(y_train)
        #numy与d分别为分子与分母
        num=0.0
        d=0.0
        for x,y in zip(x_train,y_train):
            num+=(x-x_mean)*(y-y_mean)
            d+=(x-x_mean)**2
        
        self.a_=num/d
        self.b_=y_mean-self.a_*x_mean
        return self
    
    def predict(self,x_predict):
        assert x_predict.ndim==1,\
        "简单线性回归只能解决二维问题,请输入一维向量"
        assert self.a_ is not None and self.b_ is not None,\
        "请先调用fit函数训练回归模型"
        
        return np.array([self.predict_(x) for x in x_predict])
    
    def predict_(self,x):
        #返回预测结果
        return self.a_*x+self.b_

接着测试一下,先随机生成测试数据

%matplotlib inline
x_train=np.random.randint(1,10,10)
y_train=np.random.randint(1,10,10)
plt.scatter(x_train,y_train)
plt.axis([0,11,0,11])
plt.show()

然后训练模型

SLR=SimpleLinearRegression1()
SLR.fit(x_train,y_train)
x_test=np.array([1,10])
y_predict=SLR.predict(x_test)
plt.scatter(x_train,y_train)
plt.plot(x_test,y_predict,color="red")
plt.axis([0,11,0,11])
plt.show()
x_test,y_predict

向量化运算

由于for循环效率不高,考虑引入向量化运算
\frac{\sum{w(i)*v(i)}}{\sum{w(i)*w(i)}}=\frac{w*v}{w*w}
这里的乘法为逐项相乘,即点乘,两个向量点乘结果为一个数
w向量即为(x-\overline{x})
v向量即为(y-\overline{y})

import numpy as np
import matplotlib.pyplot as plt
class SimpleLinearRegression2:
    
    def __init__(self):
        #初始化斜率a和截距b
        self.a_=None
        self.b_=None
        
    def fit(self,x_train,y_train):
        #根据训练数据集训练模型,即求出回归方程的a和b
        assert x_train.ndim==1,\
        "简单线性回归只能解决特征数为1的问题"
        assert len(x_train)==len(y_train),\
        "x_train与y_train的长度必须相同"
        
        x_mean=np.mean(x_train)
        y_mean=np.mean(y_train)
        #numy与d分别为分子与分母
        num=(x_train-x_mean).dot(y_train-y_mean)
        d=(x_train-x_mean).dot(x_train-x_mean)
        
        self.a_=num/d
        self.b_=y_mean-self.a_*x_mean
        return self
    
    def predict(self,x_predict):
        assert x_predict.ndim==1,\
        "简单线性回归只能解决二维问题,请输入一维向量"
        assert self.a_ is not None and self.b_ is not None,\
        "请先调用fit函数训练回归模型"
        
        return np.array([self.predict_(x) for x in x_predict])
    
    def predict_(self,x):
        #返回预测结果
        return self.a_*x+self.b_
    
    def __repr__(self):
        return "SimpleLinearRegression2()"

随机生成一组基本线性的测试数据

m=100000
big_x=np.random.random(size=m)
big_y=big_x*2.0+3.0+np.random.normal()#噪音

测试下效率

%%time
SLM=SimpleLinearRegression2()
SLM.fit(big_x,big_y)

运行时间为3.99ms,效率相当高

衡量线性回归准确度的指标

目标:找到a和b,使得\sum{(y_{train}-y_{train-hat})^2}尽可能小
衡量标准:\sum{(y_{test}-y_{test-hat})^2},但是这个式子还有测试样本数m的干扰,于是就出现了均方误差MSE(Mean Square Error)
MSE:\frac{\sum_{i=1}^{m}{(y_{test}-\hat{y_{test}})^{2}}}{m}
mse_test=np.sum((y_predict-y_test)**2)/len(y_test)
均方根误差RMSE(Root Mean Square Error)
RMSE:\sqrt{\frac{\sum_{i=1}^{m}{(y_{test}-\hat{y_{test}})^{2}}}{m}}
rmse_test=sqrt(mse_test)
平均绝对误差MAE(Mean absolute error)
MAE:\frac{1}{m}\sum_{i=1}^{m}\arrowvert y_{test} (i)-\hat{y_{test}}(i) \arrowvert
mae_test=np.sum(np.absolute(y_predict-y_test))/len(y_test)

一般来说,RMSE要比MAE要大一些

sklearn中也有封装MSE和MAE

调用方法如下

from sklearn.metrics import mean_squared_error as mse
from sklearn.metrics import mean_absolute_error as mae
mse(y_test,y_predict)
mae(y_test,y_predict)

除了这些指标,最好的衡量方法为

R Squared

R^{2}=1-\frac{\sum_{i}({\hat{y^{(i)}}-y^{(i)}})^2}{\sum_{i}({\overline{y}-y^{(i)}})^2}
首先R^2必须小于等于1,并且越大越好,当完全不犯错误时等于1,当其小于0时,说明数据很可能不存在线性关系
将式子变形:
R^{2}=1-\frac{\sum_{i}({\hat{y^{(i)}}-y^{(i)}})^2/m}{\sum_{i}({\overline{y}-y^{(i)}})^2/m}
分子为mse,分母为方差var
所以其可以写成1-mse/var

from sklearn.metrics import mean_squared_error as mse
import numpy as np
def Rsquared(y_test,y_predict):
    return 1-mse(y_test,y_predict)/np.var(y_test)

多元线性回归

\hat{y}^{(i)}=\theta_0+\theta_1X_1^{(i)}+\theta_2X_2^{(i)}+\theta_3X_3^{(i)}+...+\theta_nX_n^{(i)}
变成向量化运算,即\hat{y}=X_b\theta
回归的\theta向量经过求导运算,得到正规方程解\theta=(X_b^TX_b)^{-1}X_b^Ty
其中\theta=\begin{pmatrix}\theta_0 \\ \theta_1 \\ \theta_2 \\ \theta_3 \\ ... \\ \theta_n\end{pmatrix}
\theta_0为截距,其他为特征的系数,一般来说我们将两者分开,Xb为特征向量前加一列1,代表截距的系数向量
代码实现:

class LinearRegression:
    def __init__(self):
        self.coef_ = None
        self.intersection_ = None
        self._theta = None
        
    def r2_score(self,y_test,y_predict):
        return 1-mse(y_test,y_predict)/np.var(y_test)
    
    def fit_normal(self,X_train,y_train):
        """根据训练的特征向量X_train以及y_train训练模型"""
        X_b=np.hstack([np.ones((len(X_train),1)),X_train])
        self._theta=np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train)
        
        self.intersection_=self._theta[0]
        self.coef_=self._theta[1:]
        
        return self
    
    def predict(self,X_predict):
        """根据X_train,返回X_predict结果向量"""
        X_b=np.hstack([np.ones((len(X_predict),1)),X_predict])
        return X_b.dot(self._theta)
    
    def score(self,X_test,y_test):
        """测试当前模型的精准度"""
        y_predict=self.predict(X_test)
        return self.r2_score(y_test,y_predict)
    
    def __repr__(self):
        return "LinearRegression()"

用波士顿房价的数据测试一下多元回归算法:

from sklearn import datasets
import matplotlib.pyplot as plt
boston=datasets.load_boston()
X=boston.data
y=boston.target
X=X[y<50.0]
y=y[y<50.0]

X_train,X_test,y_train,y_test=train_test_split(X,y,seed=666)
reg = LinearRegression()
reg.fit_normal(X_train,y_train)

reg.score(X_test,y_test)
#0.8129802602658537

3.梯度下降法

最小化损失函数的方法,一种基于搜索的最优化算法。
对于一个图像J某一个点的导数,导数的符号代表J增大的方向。但是我们需要的是其减小的方向,所以一般对它乘一个-\beta,代表反向的一个步长。
这个参数我们称位学习率,它的取值影响获得最优解的速度,它取值不合适,就得不到最优解,并且它也是一个超参数。
但是并不是每一个函数都有极值点。 所以它有全局最优解和局部最优解两种。
但实际上并实际情况中一般不是一维的,-\eta \frac{dJ}{d\theta}
其中\theta是一个向量,代表了所有的特征,即对每一个特征求偏导,求导的部分即为梯度,J为损失函数。
在线性回归中,我们的目标是使\sum_{i=1}^m{(y^{(i)}-\hat{y^{(i)}})^2}尽可能小,即使\sum_{i=1}^m{(y^{(i)}-\theta_0-\theta_1X_1-...-\theta_nX_n)^2}尽可能小,对每一个维度的未知量求偏导。由于求导后相当于对每一个特征求m次和,所以在J前乘以1/m,乘了之后目标函数即为mse。
代码实现:

def J(theta,X_b,y):
    try:
        return np.sum((y-X_b.dot(theta))**2)/len(X_b)
    except:
        return float('inf')

def dJ(theta,X_b,y):
    res=np.empty(len(theta))
    res[0]=np.sum(X_b.dot(theta)-y)
    for i in range(1,len(theta)):
        res[i]=(X_b.dot(theta)-y).dot(X_b[:,i])
    return res*2/len(X_b)

def gradient_descent(X_b,y,initial,eta,n_iters=1e4,epsilon=1e-8):
    theta=initial_theta
    i_iter=0
    theta_history=[]
    theta_history.append(initial_theta)
    
    while i_iter<n_iters:
        gradient=dJ(theta,X_b,y)
        last_theta=theta
        theta=theta-eta*gradient
        theta_history.append(theta)
        
        if(abs(J(theta,X_b,y)-J(last_theta,X_b,y))<epsilon):
            break
        i_iter+=1
    return theta

将梯度下降法进行向量化

梯度里的式子可以看作下面两个矩阵的点乘
\frac{2}{m}*(X_b^{(1)}\theta-y^{(1)},X_b^{(2)}\theta-y^{(2)},...,X_b^{(m)}\theta-y^{(m)})

\begin{bmatrix} X_0^{(1)} & X_1^{(1)} & ... & X_n^{(1)} \\ ... & ... & ... & ... \\ X_0^{(m)} & X_1^{(m)} & ... & X_n^{(m)} \end{bmatrix}

主成分分析法

主要作用是降维,目的是找到让样本间距最大的轴。
运用方差来定义样本间距,方差越大代码样本间距越稀疏。
var(x)=\frac{1}{m}\sum_{i=1}^m{(x_i-\bar{x})^2}
首先,对所有样本demean,找到一个轴的方向w=(w_1,w_2),然后再对映射后的样本向量求方差,即var(X_{project})=\frac{1}{m}\sum{|| X_{project}^{(1)} ||^2}最大,X^{(i)}w=||X^{(i)}|| ||w||cos\theta由于w为方向向量,所以模为1。
最终方差化为\frac{1}{m}\sum{(X^{i}w)^2}最大
使用梯度上升法,对方差(向量和的平方,由于其含有m个元素,所以为m个向量的向量和)的每一个维度进行求偏导
最终求得f的梯度:\frac{2}{m}X^{T}(Xw)

def demean(X):
    return X-np.mean(X,axis=0)

def f(w,X):
#计算方差
    return np.sum((X.dot(w)**2))/len(X)

def df(w,X):
#计算目前的w的导数
    return X.T.dot(X.dot(w))*2./len(X)

def df_debug(w,X,epsilon=0.0001):
    res=np.empty(len(w))
    for i in range(len(w)):
        w_1=w.copy()
        w_1[i]+=epsilon
        w_2=w.copy()
        w_2[i]-=epsilon
        res[i]=(f(w_1,x)-f(w_2,x))/(2*epsilon)
    return res

def direction(w):
    return w/np.linalg.norm(w)

def gradient_ascent(df,x,initial_w,n_iters=1e4,epsilon=1e-8):
    
    w=direction(initial_w)
    cur_iter=0
    
    while cur_iter<n_iter:
        gradient=df(w,X)
        last_w=w
        w=w+eta*gradient
        w=direction(w)
        if(abs(f(w,X)-f(last_w,X))<epsilon):
            break
        
        cur_iter+=1
    
    return w
initial_w=np.random.random(X.shape[1])#不能从0向量开始
eta=0.001
gradient_ascent(df_debug,X_demean,initial_w,eta)
gradient_ascent(df,X_demean,initial_w,eta)

df_debug与df得到的结果相同

逻辑回归算法

逻辑回归:解决分类问题
将样本的特征和样本发生的概率联系起来\hat{y}=f(x),在逻辑回归中,\hat{y}=\hat{p}
if \hat{p}>0.5 \hat{y}=1 else \hat{y}=0
且一般用于解决二分类问题。
Sigmoid函数
\hat{p}=\sigma(\theta^Tx_b)
\sigma(t)=\frac{1}{1+e^{-t}}

def sigmoid(t):
    return 1/(1+np.exp(-t))

这个函数的值域在0与1之间,将可以将\hat{y}转化为在0与1之间的\hat{p},当t=0时,p=0.5
最终化为:
\sigma(t)=\frac{1} {1+e^{-\theta^Tx_b}}
现在问题就是如何找到参数\theta,可以最大程度获得样本X对应的分类y

损失函数

cost=(y=1 p越小 cost越大)(y=0 p越大 cost越大)
cost(y=1)=-log(\hat{p})
cost(y=0)=-log(1-\hat{p})
将这个分段函数合成:
cost=-ylog(\hat{p})-(1-y)log(1-\hat{p}))
损失函数:J(\theta)=-\frac{1}{m}\sum_{i=1}^m{cost(i)}
带入Sigmoid函数
J(\theta)=-\frac{1}{m}\sum_{i=1}^m{ylog(\sigma(X_b\theta)+(1-y)log(1-\sigma(X_b\theta)))}
但是这个函数没有公式解,只能用梯度下降法求解。
梯度即对J(\theta)的每一个\theta_i求偏导。
忽略推导过程,
J^{'} {(\theta)}=\frac{1}{m} X_b^T ( \sigma ( X_b \theta )-y)
逻辑回归实现:

import numpy as np
class LogisticRegression:
    def __init__(self):
        self.coef_ = None
        self.intercept_ = None
        self._theta = None
        
    def _sigmoid(self,t):
        return 1. /(1.+np.exp(-t))
    
    def fit(self,X_train,y_train,eta=0.01,n_iters=1e4):
        def J(theta,X_b,y):
            y_hat=self._sigmoid(X_b.dot(theta))
            try:
                return - np.sum(y*np.log(y_hat)+(1-y)*np.log(1-y_hat))/len(y)
            except:
                return float('inf')

        def dJ(theta,X_b,y):
            return X_b.T.dot(self._sigmoid(X_b.dot(theta))-y)/len(X_b)

        def gradient_descent(X_b,y,initial,eta,n_iters=1e4,epsilon=1e-8):
            theta=initial_theta
            cur_iter=0
    
            while cur_iter<n_iters:
                gradient=dJ(theta,X_b,y)
                last_theta=theta
                theta=theta-eta*gradient
                if(abs(J(theta,X_b,y)-J(last_theta,X_b,y))<epsilon):
                    break
                cur_iter+=1
            return theta
        X_b=np.hstack([np.ones((len(X_train),1)),X_train])
        initial_theta=np.zeros(X_b.shape[1])
        self._theta=gradient_descent(X_b,y_train,initial_theta,eta,n_iters)
        self.intercept_=self._theta[0]
        self.coef_=self._theta[1:]
        
        return self
    
    def predict_proba(self,X_predict):
        """根据X_train,返回X_predict结果概率向量"""
        X_b=np.hstack([np.ones((len(X_predict),1)),X_predict])
        return self._sigmoid(X_b.dot(self._theta))
    
    def predict(self,X_predict):
        """根据X_train,返回X_predict结果向量"""
        proba=self.predict_proba(X_predict)
        return np.array(proba>=0.5,dtype='int')
    
    def score(self,X_test,y_test):
        """测试当前模型的精准度"""
        y_predict=self.predict(X_test)
        return np.sum(y_test==y_predict)/len(y_test)
    
    def __repr__(self):
        return "LogisticRegression()"

决策边界

\hat{p}=\sigma (\theta^Tx_b),当\hat{p}>=0时,y=1,小于0时y=0。所以我们称\theta^Tx=0为决策边界。
假如X有2个特征,即
\theta_0+\theta_1 x_1+\theta_2 x_2=0
那对于不规则的决策边界呢?
即将所有的同类的点划为一个类,这个类的边界即为决策边界。
将线性逻辑回归改为多项式逻辑回归,degree代表阶数

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
def PolynomialLogisticRegression(degree):
    return Pipeline([
            ('ploy',PolynomialFeatures(degree=degree)),
            ('std_scaler',StandardScaler()),
            ('log_reg',LogisticRegression())
        ])

多项式回归

例如对y=ax^2+bx+c,可以将x^2作为一个新的特征

x=np.random.uniform(-3,3,size=100)
X=x.reshape(-1,1)
y=0.5*x**2+x+2+np.random.normal(0,1,size=100)
X2=np.hstack([X,X**2])
from sklearn.linear_model import LinearRegression
lin_reg2=LinearRegression()
lin_reg2.fit(X2,y)
y_predict2=lin_reg2.predict(X2)
plt.scatter(x,y)
plt.plot(np.sort(x),y_predict2[np.argsort(x)],color='r')
plt.show()

sklearn中的多项式回归和Pipeline

from sklearn.preprocessing import PolynomialFeatures
poly=PolynomialFeatures(degree=2)
poly.fit(X)
X2=poly.transform(X)#转换为多项式特征
X2.shape
#(100,3),第一列全为1,代表常数项x的阶数

若有两个特征,degree=2
x1,x2->1,x1,x2,x12,x22,x1x2,即总共五项。
Pipeline可以一次实现三步:
1.多项式化
2.数据归一化
3.线性回归
管道函数传入一个列表,列表由三个元组构成,分别是poly,std_scaler和lin_reg

from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
poly_reg=Pipeline([
        ("poly",PolynomialFeatures(degree=2)),
        ("std_scaler",StandardScaler()),
        ("lin_reg",LinearRegression())
    ])
poly_reg.fit(X,y)
y_predict=poly_reg.predict(X)

过拟合 欠拟合

对于degree的取值,虽然degree值越高,均方误差越低,但是实际显然并不是这样,它为了拟合所有的样本点变得太过复杂,我们称位过拟合。但是直接用直线来拟合,又太过简单了,我们称位欠拟合。

泛化能力

即模型面对新的数据的预测能力
所以训练集与测试集的分离是十分有必要的。

验证数据集与交叉验证

为了防止对测试数据集过拟合,我们将整个数据集分为训练数据,验证数据,测试数据。其中测试数据不参与模型的创建。其中验证数据集用于调整超参数。
但是这样也有问题,所以引入了交叉验证,将训练数据分为三份A、B、C,将BC训练,A验证,AC训练,B验证,AB训练,C验证,将这k个模型的均值作为结果。

偏差方差平衡

偏差与方差是有区别的,模型误差=偏差(Bias)+方差(Var)+不可避免的误差(噪音)
导致偏差的原因:对问题本身的假设不正确,即欠拟合
导致方差的原因:数据的一点点扰动就会影响模型,即过拟合
kNN就是天生的高方差算法(非参数学习),多项式回归是高偏差算法(参数学习)。但是偏差和方差是可以调整的,通过调整knn中的k,线性回归中的多项式回归等。
解决方差的通常手段:
1.降低模型复杂度
2.减少数据维度
3.增加样本数
4.使用验证集

模型正则化:限制参数的大小

加入模型正则化,使损失函数J(\theta)=MSE(y,\hat{y};\theta)+\alpha\frac{1}{2}\sum_{i=1}^{n}\theta_i^2,让所有的\theta尽可能小。其中\alpha是一个新的超参数
这样模型正则化的方式称为岭回归

from sklearn.linear_model import Ridge
def RidgeRegression(degree,alpha):
    return Pipeline([
            ("poly",PolynomialFeatures(degree=degree)),
            ("std_scaler",StandardScaler()),
            ("ridge_reg",Ridge(alpha=alpha))
        ])
ridge_reg=RidgeRegression(8,0.000001)
ridge_reg.fit(X_train,y_train)
ridge_reg.predict(X_test)

在逻辑回归中使用正则化

sklearn中的使用方式:
C*J(\theta)+L_1
C*J(\theta)+L_2

PCA

将高维数据向低维数据映射
假如说原来的数据为MN个矩阵,将原来的m行映射后得到主成分分析后的m个特征,然后取前k个主成分。
X·W_k^T,最终变为m
k的矩阵,含有m个样本,含有k个元素。

class PCA:
    def __init__(self,n_components):
        self.n_components=n_components
        self.components_=None
        
    def fit(self,X,eta=0.01,n_iters=1e4):
        def demean(X):
            return X-np.mean(X,axis=0)
        
        def f(w,X):
            return np.sum((X.dot(w)**2))/len(X)
        
        def df(w,X):
            return X.T.dot(X.dot(w))*2./len(X)
        
        def direction(w):
            return w/np.linalg.norm(w)
        
        def first_component(X,initial_w,eta=0.01,n_iters=1e4,epsilon=1e-8):
            w=direction(initial_w)
            cur_iter=0
            
            while cur_iter<n_iters:
                gradient=df(w,X)
                last_w=w
                w=w+eta*gradient
                w=direction(w)
                if (abs(f(w,X)-f(last_w,X))<epsilon):
                    break
                cur_iter+=1
            return w
        X_pca=demean(X)
        self.components_=np.empty(shape=(self.n_components,X.shape[1]))
        for i in range(self.n_components):
            initial_w=np.random.random(X_pca.shape[1])
            w=first_component(X_pca,initial_w,eta,n_iters)
            self.components_[i,:]=w
            
            X_pca=X_pca-X_pca.dot(w).reshape(-1,1)*w
        return self
    
    def transform(self,X):
        """将给定的X映射到各个主成分中"""
        return X.dot(self.components_.T)
    
    def inverse_transform(self,X):
        """将给定的X,反向映射回原来的空间"""
        return X.dot(self.components_)
    
    def __repr__(self):
        return "PCA(n_components=%d)"% self.n_components

LASSO Regression

与岭回归不同,LASSO的损失函数加上的是\theta的绝对值之和乘以\alpha,它趋向于使得一部分theta等于0,可以当作一种特征选择的方法。

L1 L2和弹性网络

L_p范数:||x||_p=(\sum|x_i|^p)^{\frac{1}{p}},所以岭回归相当于给损失函数加上L2正则项,LASSO回归相当于加上L1正则项。
弹性网:J(\theta)=MSE+r\alpha L1+(1-r)\alpha L2即结合了L1和L2的回归方法。

分类准确度的问题

对于极度偏斜(Skewed Data)的数据,只用分类准确度远远不够。

混淆矩阵Confusion Matrix

对于二分类问题,只需一个2*2的矩阵。
其中行代表预测值,列代表真实值。
0 - Negative
1 - Positive

0 1
0 预测Negative正确(TN) 预测Positive错误(FP)
1 预测Negative错误(FN) 预测Positive正确(TP)

精准率和召回率

精准率:precision=\frac{TP}{TP+FP}
召回率:recall=\frac{TP}{TP+FN}

F1 Score

兼顾精准率和召回率,使用精准率和召回率的调和平均值,使用调和平均值可以防止极端值出现。
F1=\frac{2*precision*recall}{precision+recall}

def f1_score(precision,recall):
    try:
        return 2*precision*recall/(precision+recall)
    except:
        return 0.0

支撑向量机(support vector machine)

期望找到的决策边界的泛化能力尽可能好,要找到一条决策边界,使其离分类样本尽可能远。SVM算法要最大化margin,解决的是线性可分问题。
hard-margin决策边界:
w^Tx+b=0,其中w^T为多维特征的不同参数。
其中
w^Tx^{(i)}+b>=1,y^{(i)}=1
w^Tx^{(i)}+b<=-1,y^{(i)}=-1
这两个不等式可以组合成,意思是margin里一个数据都没有。
y^{(i)}(w^Tx^{(i)}+b)>=1
对于任意支撑向量,要最大化max\frac{|w^Tx+b|}{||w||},即max\frac{1}{||w||},即min||w||,一般来说最小化的对象是min\frac{1}{2}||w||^2,这样方便求导。这里的最优化问题是有条件的,条件为y^{(i)}(w^Tx^{(i)}+b)>=1,要使用拉普拉斯算子。
soft-margin决策边界:
对于线性不可分的数据,将条件改为y^{(i)}(w^Tx^{(i)}+b)>=1-\eta_i,最小化方程变为:min\frac{1}{2}||w||^2+C\sum_{i=1}^m\eta_i,相当于加入了L1正则,避免了过拟合。同样也有L2正则,即\eta^2求和。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容