参考内容:
《机器学习实战》原作者github:https://github.com/ageron/handson-ml
加州房价预测项目精细解释https://blog.csdn.net/jiaoyangwm/article/details/81671084
DataFrame.where:
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.where.html
快速学会pandas中Dataframe索引.ix,.iloc,.loc的使用以及区别https://blog.csdn.net/qq1483661204/article/details/77587881
为了防止数据窥探偏误(data snooping bias),一般会将机器学习的数据集划分成三个子集:训练集,验证集和测试集。
训练集是用来训练模型的,给模型输入和对应的输出,让模型学习它们之间的关系。
验证集是用来估计模型的训练水平,比如分类器的分类精确度,预测的误差等,我们可以根据验证集的表现来选择最好的模型。
测试集是输入数据在最终得到的模型得到的结果,是训练好的模型在模拟的“新”输入数据上得到的输出。(只能在最后用于测试模型的性能,不能拿来训练。)
常见的划分比例是:50%用于训练,25%用于验证,25%用于测试。(或60%、20%、20%),这个比例可以根据数据集的大小和数据信噪比来改变。
该项目只分成训练集和测试集两个部分,选取20%的数据作为测试集。
(1)简单划分
通过创建split_train_test函数,实现将数据集分成训练集和测试集两个部分的目标:
import numpy as np
def split_train_test(data, test_ratio):
shuffled_indices = np.random.permutation(len(data))
test_set_size = int(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]
train_set,test_set = split_train_test(housing,0.2)
print(f"{len(train_set)} train + {len(test_set)} test")
# print(len(train_set), "train +", len(test_set), "test")
但是这样简单的划分数据集的话,每运行一次可能会产生不同的训练集和测试集。运行次数多的话,可能整个数据集都被运用,没能起到测试集的作用。
解决方案:
1) 在第一次运行程序后就保存数据集,后面的运行只是加载它;
2)为了保证在下一次获得更新的数据时不会中断,常见的解决方法是每个实例都使用一个标识符(identifier)来决定是否进入测试集;
3)通过Scikit-Learn提供的函数train_test_split的random_state参数,设置一个随机数生成器的种子,从而让它始终生成相同的随机索引。例:random_state = 42;42出自《银河系搭车客指南》的“关于生命、宇宙和一切的终极问题的答案”。
(2)使用标识符(identifier)划分
housing数据集没有标识符列,最简单的是使用行号索引作为ID:
housing_with_id = housing.reset_index() #新增一个“index”列
train_set, test_set = split_tain_test_by_id(housing_with_id, 0.2, "index")
使用这种方法需要确保在数据集的末尾添加新数据,并不会删除任何行。
如果没法保证使用行索引使用要求的话,可以尝试使用一个最稳定的特征来创建唯一标识符(本例选择经纬度):
housing_with_id["id"] = housing["longitude"] * 100 + housing["latitude"]
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "id")
(3)使用Scikit-Learn提供的函数train_test_split
通过Scikit-Learn提供的函数train_test_split的random_state参数,设置一个随机数生成器的种子,从而让它始终生成相同的随机索引。
from sklearn.model_selection import train_test_split
train_set,test_set = train_test_split(housing, test_size = 0.2, random_state = 42)
print(f"{len(train_set)} train + {len(test_set)} test")
纯随机的取样方法在数据集很大的时候(尤其是相较于属性的数量而言),通常是可行的。但是如果数据集不大,可能会导致明显的抽样偏差。
数据少的时候采用分层抽样,从每个分层去取合适数量的实例,能相对保证测试集对总人数具有代表性。
专家建议收入中位数(median_income)是一个非常重要的属性,因此可以以这一属性作为分层抽样的依据。(随机切分会将原本的数据分布破坏掉,比如收入、性别等,保证特性分布是很重要的)
(1)以中位数(median_income)创建一个分类属性
housing["median_income"].hist()
plt.show()
可以看到,收入中位数(median_income)集中在2~5(万美元)附近,超出6(万美元)的数据较少,为了保证分层抽样的每一层有足够的数据,层数不应该分的太多。
import numpy as np
housing["income_cat"] = np.ceil(housing["median_income"] / 1.5)
housing["income_cat"].where(housing["income_cat"] < 5, 5.0, inplace = True)
housing["income_cat"].hist()
plt.show()
这里通过将收入中位数(median_income)除以1.5来限制收入类别的数量,然后使用ceil取整得到离散类别,最后将所有大于5的类别合并成一个类别5。
DataFrame.where()的用法:
DataFrame.where(condition, other, inplace=False, axis=None, level=None, errors='raise', try_cast=
)
inplace = True 的参数解读:
inplace = True:不创建新的对象,直接对原始对象进行修改;
inplace = False:对数据进行修改,创建并返回新的对象承载其修改结果。默认是False,即创建新的对象进行修改,原对象不变。
(2)进行分层抽样
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(housing, housing["income_cat"]):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
housing["income_cat"].value_counts() / len(housing)
#查看住房数根据收入类别的比例分布
DataFrame.loc[]的用法:
DataFrame.loc[] 通过轴标签选择数据
DataFrame.iloc[] 通过整数索引选择数据
n_splits = n的用法:
表示划分为几块(至少是2) 例:n_splits = 1 表示分成两块
def income_cat_proportions(data):
return data["income_cat"].value_counts() / len(data)
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
compare_props = pd.DataFrame({"Overall": income_cat_proportions(housing),
"Stratified": income_cat_proportions(strat_test_set),
"Random": income_cat_proportions(test_set),}).sort_index()
compare_props["Rand. %error"] = 100 * compare_props["Random"] / compare_props["Overall"] - 100
compare_props["Strat. %error"] = 100 * compare_props["Random"] / compare_props["Overall"] - 100
compare_props
由图中可以看出,分层抽样测试集的收入分类比例与总数据集的几乎相同,而随机采样数据集偏差严重。
恢复原始数据
删除income_cat这一属性:
for set in (strat_train_set, strat_test_set):
set.drop(["income_cat"], axis = 1, inplace = True)
axis=0:在第一维操作
axis=1:在第二维操作
axis=-1:在最后一维操作