hands_on_Ml_with_Sklearn_and_TF(开始一个小项目——训练集与测试集的划分)

1.这次我想以学习笔记的形式对《hands_on_ML_with_Sklearn_and_TF》这本书进行学习,我看了许多关于机器学习或是深度学习的书籍或是资料,总体而言我觉得这本书写的很详细,不仅仅有原理的解释还有代码的操作,所以还是能学到不少知识的。网站我放在这里hands_on_ml_with_sklearn_and_tf(中文版),希望大家也都看看。

2.机器学习中,训练集与测试集的划分是一个比较重要的工作,这关系到数据集的质量。

首先我们本次利用的数据集为加利福利亚房价(即StatLib 的加州房产价格数据集),这数据结果如下表:

longitude,latitude:经纬度
housing_median_age: 房屋年龄的中位数
total_rooms: 总房间数
total_bedrooms: 卧室数量
population: 人口数
households: 家庭数
median_income: 收入中位数
median_house_value: 房屋价值中位数
ocean_proximity: 离大海的距离

首先使用urllib.request.urlretrieve()的方法从网站上获取该数据集并将其保存到csv文件中,接着我们要引入一个重要的python库——pandas以及它的一个重要数据结构DataFrame,利用pd.read_csv()的方法来读取csv文件的数据并将其赋给一个DataFrame数据的变量。代码如下:

import pandas as pd

def load_housing_data(housing_path=HOUSING_PATH):
    '''
    利用pandas读取CSV文件,返回一个相应的数据类型
    '''
    csv_path = os.path.join('./', housing_path, "housing.csv")
    data = pd.read_csv(csv_path)
    # print(data)
    return data

data = load_housing_data()

最后的data便是一个DataFrame类型的数据,其在pycharm的Data View中展示的形式为:

hands_on_Ml_with_Sklearn_and_TF(开始一个小项目——训练集与测试集的划分)_第1张图片

以上就是我们用pandas读取csv文件得到DataFrame数据的过程。

3.讲了这么多还没有进入正题,现在讲一讲我们随机数据分配。

直接附上代码:

# 2. 划分测试集与训练集(随机取样)
import numpy as np
# 2.1 自己写的划分方法
def split_train_test(data, test_ratio):
    '''
    对数据划分训练集与测试集
    :param data: 原始数据
    :param test_ratio: 测试集的大小
    :return: (训练集, 测试集)
    '''
    shuffled_indices = np.random.permutation(len(data)) # permutation(x)将x中的打乱重排
    test_set_size = round(len(data) * test_ratio)
    test_indices = shuffled_indices[:test_set_size]
    train_indices = shuffled_indices[test_set_size:]

    return data.iloc[train_indices], data.iloc[test_indices]

以上的方法是一个常用的基本方法,唯一需要记住的是np.random.permutation(len)这个方法,就是一个乱序方法。

写出上面的方法,一般而言在静态的环境下好像没什么问题,但如果在一个动态的环境下问题就来了。

1)数据集不变的情况,如果多次调用这个函数将会导致测试集每次发生变化,多次试验后就会出现测试集变成了整个数据集了。解决这个问题的方法有两种:第一,在np.random.permutation(len)的前面首先设置随机种子;第二,将测试集与训练集分别保存下来,之后只有调用这个保存的结果即可。

2)但1)只是一种情况,实际上更多的情况则是,数据集本身出现了变化。比如,数据集增大了(也可能减少了),这样1)中的方法可能就不能使用了。最后我们选择设置实例识别码的方法来解决这个问题,所以最后采用对每个实例赋予识别码,判断实例是否应该放入测试集,新的测试集将包含新实例中的20%,但是不会有之前在训练集的实例。

相应的代码如下:

def test_set_check(identifier, test_ratio, hash):
    return hash(np.int64(identifier)).digest()[-1] < 256 * test_ratio


def split_train_test_by_id(data, test_ratio, id_column, hash=hashlib.md5):
    '''
    本函数是用来通过某一列进行训练集与测试集的划分工作
    :param data: 原始数据集
    :param test_ratio: 测试集的大小
    :param id_column: 划分依照的列的属性
    '''
    ids = data[id_column]
    in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio, hash)) # 这个id_我还是没明白是怎么回事

    return data.loc[~in_test_set], data.loc[in_test_set] # 细节使用~

housing_with_id = data.reset_index() # reset_index()增加一列作为索引"index",作为行索引
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")

这个过程显得就复杂一些,首先我们要明白hashlib的使用。在test_set_check(identifier, test_ratio, hash)中我们获取了识别码中最后一个字节作为hash函数的输入值,因为总共2^8==256中可能,我们判定其中前20%为测试集,这样每次增加数据集后(这个增加的数据集必须放在后面,原因接下来会讲),原数据的20%的测试集不会发生变化,新数据的20%也会放入测试集中。这里还是用了reset_index()的方法将行号作为一个索引。

当然这种方法也会出现一个问题,他是以行索引作为识别码的,如果新加入的数据集会随机分配在数据集中,这个方法就会出现问题。这样,我们就分析数据集中有哪些数据是最稳定的不会发生任何改变。在这个数据中经纬度是几百万年都不会出现什么改变所以我们可以使用这两个属性的组合作为标识码。

代码如下:

housing_with_id["id"] = data["longitude"] * 1000 + data["latitude"]
temp_column = housing_with_id.pop("id")
housing_with_id.insert(0, "id", temp_column) # 这个就是用来在第0列插入一个索引
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "id")

这样我们就将数据集分解为训练集与测试集,当然以上的方法是随机抽取的方法,接下来我们将了解下分层分解的方法。

3.sklearn中有相关的内置函数

from sklearn.model_selection import train_test_split

train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

4. 分层抽取的方法

在步骤3中采用的方法是纯随机的取样方法,当你的数据集很大的时候这个通常可行,但如果数据不大,就会出现采样偏差的风险。比如像我么学校男女比为3:1,如果你采样的结果为1:1那么肯定会出现偏差,这样我们就需要把人群分成均匀的子分组,这个称为分层,从每个分层去除合适数量的实例。

在本实例中,要预测房屋价格的中位数,我们可以知道地区收入的中位数肯定是一个很好的参考指标。首先我们将收入中位数进行分类,代码如下:

data["income_cat"] = np.ceil(data["median_income"] / 1.5) # ceil是进行数据取舍,以产生离散的分类
# 注:inplace的含义就是如果True则是在原DataFrame上面进行相关的操作;如果是False就是新形成一个DataFrame保留原本的DataFrame
data["income_cat"].where(data["income_cat"] < 5, 5.0, inplace=True) # where实际上可以看成一个for循环与if else块的合成

这段代码的详细介绍在注释中已经写了。

对于分层抽取,我们可以利用sklearn的StratifiedShuffleSplit类:

from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)

for train_index, test_index in split.split(data, data["income_cat"]): # 这段代码就是分层获取训练集与测试集的索引
    strat_train_set = data.loc[train_index]
    strat_test_set = data.loc[test_index]

关于StratifiedShuffleSplit()中的参数我们需要注意的是n_splits:

参数 n_splits是将训练数据分成train/test对的组数,可根据需要进行设置,默认为10

参数test_size和train_size是用来设置train/test对中train和test所占的比例。例如: 
1.提供10个数据num进行训练和测试集划分 
2.设置train_size=0.8 test_size=0.2 
3.train_num=num*train_size=8 test_num=num*test_size=2 

4.即10个数据,进行划分以后8个是训练数据,2个是测试数据

接着我们计算下用这种方法得到的原数据集与训练集的分类情况:

print(data["income_cat"].value_counts() / len(data)) # 查看分层的大小
print(strat_train_set["income_cat"].value_counts() / len(strat_train_set))
3.0    0.350581
2.0    0.318847
4.0    0.176308
5.0    0.114438
1.0    0.039826
# 以上是原始数据集的收入分类情况
3.0    0.350594
2.0    0.318859
4.0    0.176296
5.0    0.114402
1.0    0.039850
# 以上是训练数据集的收入分类情况
5. 第一次学习pandas与sklearn等知识还是有点生硬,下次加油吧。

你可能感兴趣的:(hands_on_Ml_with_Sklearn_and_TF(开始一个小项目——训练集与测试集的划分))