美文网首页
机器学习

机器学习

作者: 恰似一碗咸鱼粥 | 来源:发表于2019-04-23 13:20 被阅读0次

    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求和。

    相关文章

      网友评论

          本文标题:机器学习

          本文链接:https://www.haomeiwen.com/subject/ryvygqtx.html