课本内容回顾:
分类是数据挖掘领域为常用的方法之一,不论是实际应用还是科研,都少不了它的身影。对于分类问题,我们通常能拿到表示实际对象或事件的数据集,我们知道数据集中每一条数据所属的类别,这些类别把一条条数据划分为不同的类。
数据处理在这里的目标就是根据已有的数据训练分类模型,然后可以用以对新样本的分类。
实验内容:使用著名的Iris植物分类数据集:集共有150条植物数据,每条数据都 给出了四个特征:sepal length、sepal width、petal length、petal width(分别表示萼片和花瓣的长 与宽),单位均为cm)。该数据集共有三种类别:Iris Setosa(山鸢尾)、Iris Versicolour(变色鸢尾)和Iris Virginica(维吉尼 亚鸢尾)。我们这里的分类目的是根据植物的特征推测它的种类。抽象来说,就是根据已有的植物特征(4维数组)和所属分类(0,1,2)来对新的4维数组输出一个分类值。
数据获取:
scikit-learn库内置了该数据集,可直接导入。
from sklearn.datasets import load_iris
dataset = load_iris()
X = dataset.data
y = dataset.target
数据集的特征值都是连续的,对于连续数据有个特征:差越小意味着更加相似,比如身高174和175就是很接近的数据,但是对于更多数据样本中的离散值,每个数据只是代表一个离散情况,差小不会有显然的相似关系,比如学号1和学号2的两个同学。连续值的处理更加复杂,这里为了简化代码和算法,选择将连续值先离散化进行处理。
离散方法为:
n_samples, n_features = X.shape #确定数据的维度
attribute_means = X.mean(axis=0) #求每个横坐标的均值:特征均值
assert attribute_means.shape == (n_features,) # 断言
X_d = np.array(X>=attribute_means, dtype = 'int') #构建离散数据样本:大于均值为1,否则为0
这样我们就获得了离散数据样本。
接下来正是进入算法的构造:ONER原则,即“一条规则”算法,核心原理是简化模型,使用一个最能代表类别的特征值来直接判断属于哪个类别。举个例子,我们要区分一堆学生的男女,我们只要看他们生理构造就基本够了,不需要关注其他的诸如肤色等的特征。算法的训练过程就是计算使用每一个特征值作为判断依据的错误率,然后使用错误率最低的特征值。可以预见的是,如果样本数据存在一个很具有代表性的特征值,这个方法会很好用。
1. 构建函数就算每个特征值预测的错误率
def train_feature_value(X, y_true, feature_index, value):
class_counts = defaultdict(int) # 统计特征值为Value的每种分类的数量
# 比如我们说统计100个学生中有丁丁(feature(JJ).value = 1)人中,男生多少人,女生多少人
for sample, y in zip(X, y_true): #zip函数,把两个集合按照id顺序构建pair,做新序列
if sample[feature_index] == value:
class_counts[y] += 1
# 排序特征值为value的样本所属分类的ID
sorted_class_counts = sorted(class_counts.items(), key=itemgetter(1), reverse = True)
most_frequent_class = sorted_class_counts[0][0]
#print(sorted_class_counts)[0]
# 根据实际结果计算分类错误的合计(有50个人JJ.value = 1,但是只有48个男生,error2个)
error = sum([class_count for class_value, class_count in class_counts.items() if class_value != most_frequent_class])
return most_frequent_class, error
接下来需要把训练函数写到循环里:(对于每个feature,得到一个预测列表和错误合计)
def train(X, y_true, feature):
n_samples, n_features = X.shape
assert 0<=feature<n_features
values = set(X[:,feature])
predictors = dict()
errors = []
for current_value in values:
most_frequent_class, error = train_feature_value(X, y_true, feature, current_value)
predictors[current_value] = most_frequent_class
errors.append(error)
total_error = sum(errors)
return predictors, total_error
2. 构造训练数据和验证数据:数据全部来自这个Isri的数据集,这里使用train_test_split方法直接把数据分成112组训练数据和38组验证数据
from sklearn.model_selection import train_test_split
random_state = 14
X_train, X_test, y_train, y_test = train_test_split(X_d, y, random_state=random_state)
3. 得到训练数据以后就可以开始训练:原理就是,根据不同的特征Feature循环调用Train函数,得到预测数据集,其中包含了每个feature的训练结果和error。然后根据error排序得到的最优判断方法就是我们的判断模型了。
# Compute all of the predictors
all_predictors = {variable: train(X_train, y_train, variable) for variable in range(X_train.shape[1])}
errors = {variable: error for variable, (mapping, error) in all_predictors.items()}
# Now choose the best and save that as "model"
# Sort by error
best_variable, best_error = sorted(errors.items(), key=itemgetter(1))[0]
print("The best model is based on variable {0} and has error {1:.2f}".format(best_variable, best_error))
# Choose the bset model
model = {'variable': best_variable,
'predictor': all_predictors[best_variable][0]}
这里写一点简单测试代码验证一下训练结果:
4.训练结束,跑测试数据验证结果:
def predict(X_test, model):
variable = model['variable']
predictor = model['predictor']
y_predicted = np.array([predictor[int(sample[variable])] for sample in X_test])
return y_predicted
其实能发现一个非常明显的问题,数据被我们离散化到了0 1两个点,但是状态却又0 1 2三个,因此我们的预测数据只有两个状态,预测结果准确率受到很大的影响。
5. 评估:直接上评估结果