机器学习 留出法 python实现

文章目录

  • 方法一 调用sklearn库
  • 方法二 利用random库或numpy库的不重复取样函数
  • 一些错误想法
      • 分成按一定比例的两部分 不能是期望!
      • 就算循环的每一步重新调整概率 也只是减小误差
      • 但是 是有补救方法的

方法一 调用sklearn库

from sklearn.model_selection import train_test_split
train_X, test_X, train_Y, test_Y = train_test_split(X, Y, test_size=0.2)

方法二 利用random库或numpy库的不重复取样函数

import pandas as pd
# 下面选一种 import
import random
from numpy import random

def train_test_split(X,Y=None,test_size=0.2):
    length = len(X)
    train_index = list(range(length))
    test_index = random.sample(train_index,int(length*test_size))
    for x in test_index:
        train_index.remove(x)
    if Y is not None:
        return X.iloc[train_index],X.iloc[test_index],Y.iloc[train_index],Y.iloc[test_index]
    else:
        return X.iloc[train_index],X.iloc[test_index]

一些错误想法

这是我想仅用 random.random() 函数实现这个功能时的一些错误想法,以便以后参考
实际非要用python自带库实现这个功能时,用方法二就行了

分成按一定比例的两部分 不能是期望!

以将 [0, 1, 2, 3 …] 按比例分成两部分为例,test_size直接取 0.3 。

import random
origin = list(range(20))
train = []
test = []
for x in origin:
    if random.random() < 0.7:
        train.append(x)
    else:
        test.append(x)

就算循环的每一步重新调整概率 也只是减小误差

但是能减小误差的话,说不定以后有用。
思路是增加维护一个临时概率,数值上等于已知前n个样本归类时,下一个样本归类的后验概率
n t r a i n n_{train} ntrain n t e s t n_{test} ntest 为已归类训练集、测试集样本数量, e e e 为任意一个样本属于训练集的期望(先验概率), e t m p e_{tmp} etmp 为已知前n个样本归类时,下一个样本属于训练集的后验概率, l l l 为所有样本数量。
e t m p ∗ ( l − n t r a i n − n t e s t ) + 1 ∗ n t r a i n = l ∗ e e_{tmp}*(l-n_{train}-n_{test})+1*n_{train}=l*e etmp(lntrainntest)+1ntrain=le
得到
e t m p = l ∗ e − n t r a i n l − n t r a i n − n t e s t e_{tmp}=\frac{l*e-n_{train}}{l-n_{train}-n_{test}} etmp=lntrainntestlentrain
如果想节约空间,不打算维护 n t r a i n n_{train} ntrain n t e s t n_{test} ntest,也可以推导出 e t m p e_{tmp} etmp 的递推公式,但还是得维护个 n = n t r a i n + n t e s t n=n_{train}+n_{test} n=ntrain+ntest,初值为
n 0 = 0 e t m p 0 = e n^0=0\\e_{tmp}^0=e n0=0etmp0=e
递推式为
n i  (i > 0) = n i − 1 + 1 e t m p i  (i > 0) = { e t m p i − 1 ∗ ( 1 + 1 l − n i ) − 1 l − n i last classify = train e t m p i − 1 ∗ ( 1 + 1 l − n i ) last classify = test n^i\text{ (i > 0)}=n^{i-1}+1 \\ e_{tmp}^i\text{ (i > 0)}=\left\{ \begin{array}{rcl} e_{tmp}^{i-1}*(1+\frac{1}{l-n^i})-\frac{1}{l-n^i} & & \text{last classify = train} \\ e_{tmp}^{i-1}*(1+\frac{1}{l-n^i}) & & \text{last classify = test} \end{array} \right. ni (i > 0)=ni1+1etmpi (i > 0)={etmpi1(1+lni1)lni1etmpi1(1+lni1)last classify = trainlast classify = test
很容易看出,为节约时间,我们可以换个维护变量 m i = 1 / ( l − n i ) m^i=1/(l-n^i) mi=1/(lni),这样甚至连 l l l 都不需要保存了,当代码需要优先节约空间,其次节约时间的时候,就这么做吧,初值为
m 0 = 1 l e t m p 0 = e m^0=\frac{1}{l}\\e_{tmp}^0=e m0=l1etmp0=e
递推式为
m i  (i > 0) = m i − 1 1 − m i − 1 e t m p i  (i > 0) = { e t m p i − 1 ∗ ( 1 + m i ) − m i last classify = train e t m p i − 1 ∗ ( 1 + m i ) last classify = test m^i\text{ (i > 0)}=\frac{m^{i-1}}{1-m^{i-1}} \\ e_{tmp}^i\text{ (i > 0)}=\left\{ \begin{array}{rcl} e_{tmp}^{i-1}*(1+m^i)-m^i & & \text{last classify = train} \\ e_{tmp}^{i-1}*(1+m^i) & & \text{last classify = test} \end{array} \right. mi (i > 0)=1mi1mi1etmpi (i > 0)={etmpi1(1+mi)mietmpi1(1+mi)last classify = trainlast classify = test
下面给个只维护 m m m 的代码实现

import random
origin = list(range(20))
expectation = 0.7
train = []
test = []
m = 1/len(origin)
for x in origin:
    m = m/(1-m)
    if random.random() < expectation:
        train.append(x)
        expectation = expectation*(1+m)-m
    else:
        test.append(x)
        expectation = expectation*(1+m)

测试后可以发现,效果确实大大滴好,可惜还是犯了原则性错误。

但是 是有补救方法的

其实,补救思路非常简单,也可以数学证明这个补救措施不会改变任意一个样本分类的期望。
这个补救方法就是,在维护 n t r a i n n_{train} ntrain n t e s t n_{test} ntest 的基础上,每一次循环加个判断。记 l t r a i n l_{train} ltrain l t e s t l_{test} ltest 为两个集合的最大容纳量,数值上等于 i n t ( l ∗ e ) int(l*e) int(le) l − i n t ( l ∗ e ) l-int(l*e) lint(le) 。当 n t r a i n n_{train} ntrain 等于 l t r a i n l_{train} ltrain n t e s t n_{test} ntest 等于 l t e s t l_{test} ltest 时,将剩下的样本全部放到另一个集合就可以了。

你可能感兴趣的:(python,机器学习,python,机器学习)