机器学习期末作业

 期末要求和数据集

 本篇文章是基于iris鸢尾花数据集进行探究,该数据集也是最适合数据分析的基本数据

https://pan.baidu.com/s/1HPvQM3DuyzBgAq7g3O3TBg?pwd=awsm 提取码: awsm

注意:代码文件和数据集是放在一个目录下的

参考代码

导包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import graphviz
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.cluster import KMeans
from sklearn.neighbors import KNeighborsClassifier
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score,recall_score,precision_score

 导入Iris数据集

iris_data = pd.read_csv("./iris.csv")
iris_data.head()

查看Iris数据集的维度、特征名称和特征类型 

print(f"数据集的维度:{iris_data.shape}")
#标签不是特征
print(f"特征名称:{iris_data.columns[:-1]}")
print("特征类型:\n",iris_data.dtypes)

打印所有列名

iris_data.columns

显示Iris数据集的基本统计学特性,如未缺失值的数值、均值、标准差、范围、四分位数等

#查看特征信息、未缺失的个数
iris_data.info()

#查看缺失值的个数
iris_data.isnull().sum()

#查看均值、标准差、范围、四分位
'''
count:个数
mean:每个特征的平均值
std:每个特征的标准差
min:每个特征的最小值
25%:下四分位数
50%:中位数
75%:上四分位数
max:每个特征的最大值
'''
iris_data.describe()

输出数据集 0 至10 行、1至3列 的数据

iris_data.iloc[:11,:3]

输出1至3列最大值

#axis=0表示找列的最大值,axis=0表示找行的最大值
iris_data.iloc[:,:3].max(axis=0)

根据class属性的值对数据进行分组,显示每组的统计数据,并绘制直方图

#按class列分组
groups = iris_data.groupby('class')
print(groups.groups)

#统计数据
for key in iris_data.columns[:-1]:
    print(f"每组关于{key}的统计数据")
    print(groups.describe()[key])
#统计数据也可以写成一下形式,只不过显示的信息过多,中间部分被省略
# groups.describe()
#直方图,每组个数
plt.figure(figsize=(6,4))
plt.subplot(1,1,1)
plt1 = iris_data['class'].value_counts().plot(kind='bar')
plt.show()
fig=plt.figure(figsize=(20,7))

for i,col in enumerate(iris_data.columns[:-1]):
    fig=px.histogram(data_frame=iris_data, x=col,color='class',color_discrete_sequence=['#491D8B','#7D3AC1','#EB548C'],nbins=30)
    fig.show()

d1ecaf5378c146a597f498d9720aeaa3.png

#也可以通过下面方式画出每组在每个区间的直方图
plt.figure(figsize=(20,7))

for i,col in enumerate(iris_data.columns[:-1]):
    plt.subplot(2,2,i+1)
    sns.histplot(x=iris_data[col],hue=iris_data['class'],stat="count",multiple="dodge")
plt.show()

 将Iris数据集中的数值属性进行Z-Score归一化。

z-score是使得处理过后的数据符合正态分布,公式如下:

gif.latex?x_%7Bnormalize%7D%20%3D%5Cfrac%7Bx-%5Cmu%7D%7B%5Csigma%7D

其中gif.latex?%5Cmu代表均值,gif.latex?%5Csigma代表标准差,代码如下:

x = iris_data.drop('class',axis=1)
y = iris_data['class']

def z_score(X):
    means = X.mean(axis=0)
    stds = X.std(axis=0)
    return (X - means) / stds

x = z_score(x)
print(x)

当然,这里你也可以使用导包的方式标准化,代码如下:

#也可以使用sklearn的StandardScaler包进行z_score归一化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
x = sc.fit_transform(x)
print(x)

利用k-means算法完成Iris数据集的聚类,并输出聚类结果的正确率和召回率

x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2,random_state=1)

1、直接使用sklearn中的包

kmeans = KMeans(n_clusters=3,random_state=0)
kmeans.fit(x_train) #训练模型
labels = kmeans.labels_ #获取聚类标签
print(labels)
#四维的图形无法画出,这里使用sepal length和sepal width来绘图
def plotData():
    X = x_train.values
    plt.scatter(X[:,0],X[:,1],c=labels)
    plt.xlabel('sepal length')
    plt.ylabel('sepal width')
    plt.title("Clusters Plot")
    plt.show()
    
plotData()

1ac208cace54470fa8656b9e9f8ca5ae.png

 2、手写Kmeans聚类算法

步骤:

  1. 随机初始簇中心(一般随机选择样本中的几个点作为初始簇中心)
  2. 利用欧式距离,计算每个样本点到簇中心的距离,离哪个簇中心最近就将其归属于该簇
  3. 分好簇后,求每个簇的特征均值作为新的簇中心
  4. 直到簇中心不再发生变化,就停止算法

1、计算欧式距离

#计算欧式距离
def compute_distance(x,y):
    return np.sqrt(np.sum((x - y) ** 2))

2、计算每个样本到簇中心的距离,返回每个样本对应的簇的索引列表 

def find_closet_centroids(X,centroids):
    #
    m = X.shape[0]
    idx = np.zeros(m,dtype=int)
    
    #簇中心的个数
    k = len(centroids)
    for i in range(m):
        min_dist = 1000000.
        #遍历每个簇中心,找出距离最短的簇中心
        for j in range(k):
            dist = compute_distance(X[i],centroids[j])
            if dist < min_dist:
                min_dist = dist
                idx[i] = j #属于第几个簇
    return idx

3、分好簇后,更新簇中心

#均值作为分类好的簇将会重新更新簇中心
def compute_centroid(X,idx,K):
    n = X.shape[1]
    centroids = np.zeros((K,n))
    
    for j in range(K):
        #找到每一个簇的样本
        Xi = X[np.where(idx == j)]
        m = len(Xi)
        centroids[j] = np.mean(Xi,axis=0)
    return centroids

4、画图

def plot_KMean(X,centroids,idx):
    plt.scatter(X[:,0],X[:,1],c=idx,marker='+',cmap='rainbow')
    plt.scatter(centroids[:,0],centroids[:,1],s=80,marker='o',c='b')
    for i in range(len(centroids)):
        plt.annotate('center',xy=(centroids[i,0],centroids[i,1]),xytext=\
        (centroids[i,0]+0.5,centroids[i,1]+1),arrowprops=dict(facecolor='yellow'))

 5、kmeans聚类算法

def run_Kmean(X,K,max_iters=10):
    m,n = X.shape
    #随机打乱选取簇中心
    rand = np.random.permutation(X.shape[0])
    centroids = X[rand[:K]]
    idx = np.zeros(m)
    
    for i in range(max_iters):
        idx = find_closet_centroids(X,centroids)
        
        centroids = compute_centroid(X,idx,K)
        
    return centroids,idx

6、运行

X_train = x_train.values
Y_train = y_train.values
X_test = x_test.values
Y_test = y_test.values
K = 3
max_iters = 10
centroids,idx = run_Kmean(X_train,K,max_iters)
plot_KMean(X_train,centroids,idx)

运行结果如下:

64f7d98be96c4f989c7d7dc130bdc76a.png

3、 准确率和召回率

说实话,我也不明白这里为什么计算准确率,因为Kmeans聚类是无监督学习,没有真实标签参考,可能这里给出了真实标签。好吧,对于这部分我现在也一头雾水,大概说下我的思路吧:

  1. 从已知的训练集中各找出一个类别不同的样本点
  2. 计算这几个点到最后我们求得的簇中心的距离
  3. 离簇中心最近的样本点,就是该簇中心的标签
  4. 测试集分别计算到簇中心的距离,将其分配给距离最近的簇
  5. 然后计算精确率和召回率

代码如下:

#这里可能有不足之处,那就是以下的代码我假设了聚类的效果为最佳,这其实是不符合现实条件的
label = ['setosa','versicolor','virginica']
targets = []
for i in centroids[:-1]:
    min_dist = 100000
    target = -1
    for l in label:
        xl = X_train[np.where(y_train == l)][0]
        dist = compute_distance(xl,i)
        if dist < min_dist:
            min_dist = dist
            target = l
    targets.append(target)
    label.remove(target)
targets.append(label[0])
print(targets)
def predict(X,centroids,targets):
    m = len(X)
    cen = len(centroids)
    pred = []
    #遍历每一个测试样本
    for i in range(m):
        #有三个簇中心,计算到每个簇中心的距离
        dists = np.zeros(cen)
        for j in range(cen):
            dists[j] = compute_distance(X[i],centroids[j])
        #找到最小的距离
        index = np.argmin(dists)
        #index表示距离最小的索引
        pred.append(targets[index])
    return pred
predicts = predict(X_test,centroids,targets)
def score(Y,predicts):
    precision = 0
    accuracy = 0
    recall = 0
    #遍历每一个类别,计算每一个的精确率、召回率
    for p in np.unique(Y):
        #构造每一个类别的混淆矩阵
        confusion_metric = np.zeros((2,2))
        for i in range(len(predicts)):
            pred = predicts[i]
            if Y[i] == p and pred == p:
                confusion_metric[0][0] += 1
            if Y[i] != p and pred == p:
                confusion_metric[0][1] += 1
            if Y[i] == p and pred != p:
                confusion_metric[1][0] += 1
            if Y[i] != p and pred !=p:
                confusion_metric[1][1] += 1
    #     print(confusion_metric)

        #对照上面的混淆矩阵即可
        TP = confusion_metric[0][0]
        FP = confusion_metric[0][1]
        FN = confusion_metric[1][0]
        TN = confusion_metric[1][1]

        precision += TP / (TP + FP)
        accuracy += (TP + TN) / (TP + FP + FN + TN)
        recall += TP / (TP + FN)
    return precision,accuracy,recall
    
precision,accuracy,recall = score(Y_test,predicts)
print(f"Kmeans精确率:{precision / 3:.3f}")
print(f"Kmeans准确率:{accuracy / 3:.3f}")
print(f"Kmeans召回率:{recall / 3:.3f}")

1ccd9bbb742245d98198e167c66d0549.png

 利用相关决策树算法构建Iris数据集的决策树并图形化显示。输出测试集分类结果的正确率和召回率

这里依旧有两种方式可选

1、调用sklearn.tree的包

clf = DecisionTreeClassifier()
#使用网格化搜索,找到最优参数
params = {'criterion':['gini','entropy'],'max_depth':np.arange(2,10)}
grid = GridSearchCV(clf,param_grid=params,cv=3).fit(x_train,y_train)
print("the best params for DecisionTree is:",grid.best_params_,",the score is:",grid.best_score_)

计算准确率和召回率

clf = DecisionTreeClassifier(criterion='gini',max_depth=3)
clf.fit(x_train,y_train)
#预测测试集结果
y_pred = clf.predict(x_test)
print("精确率:%.3f"%precision_score(y_test,y_pred,average='micro'))
print("准确率:%.3f"%accuracy_score(y_test,y_pred))
print("召回率:%.3f"%recall_score(y_test,y_pred,average='micro'))

4415d583439e49118462ab478744c710.png

 画出树,调用tree.plot_tree

plt.figure(figsize=(12,10))
tree.plot_tree(clf.fit(x_train,y_train),
               feature_names=x_train.columns,
               class_names=np.unique(y_train))

27f7ed3a54d44c868e17ba5bef9a9169.png

 也可以画彩色的,代码如下:

dot_data = tree.export_graphviz(clf,out_file=None,
                               feature_names=x_train.columns,
                               class_names=np.unique(y_train),
                               filled=True,
                               rounded=True,
                               special_characters=True)
graph = graphviz.Source(dot_data)
graph

运行效果:

eeb6328707ec41ffa0e4e81074e87998.png

 检查是否欠拟合和过拟合

  1. 欠拟合:训练集和测试集的得分都不高
  2. 过拟合:在训练集上表现很好,但是测试集表现很差,即训练集的得分远高于测试集的得分
#检查是否欠拟合或者过拟合
print(f"Training set score: {clf.score(x_train,y_train)}")
print(f"Test set score: {clf.score(x_test,y_test)}")

#运行结果
#Training set score: 0.9833333333333333
#Test set score: 0.9666666666666667

2、手写决策树(以CART决策树为例)

CART决策树采用Gini系数来选择最优的特征划分

Gini系数:gif.latex?Gini%3D1-%5Csum%20p%5E2

Gini系数越小越好,因为gif.latex?p%5E2越大,说明该样本的纯度越高

步骤

  1. 计算Gini系数
  2. 遍历每个特征,计算gif.latex?Gini%28D%2CA%29%3D%5Cfrac%7B%7CD_1%7C%7D%7B%7CD%7C%7DGini%28D_1%29&plus;%5Cfrac%7B%5BD_2%5D%7D%7B%7CD%7C%7DGini%28D_2%29找到Gini系数最小的最优划分特征。对于连续型的处理,我们通常选择一个阈值来划分,这个阈值的选取是:先对该特征下的取值进行排序,选择前后每两个点的平均值作为划分阈值的备选
  3. 找到最优分类特征后,重复1.、2.步骤,递归构建左右子树
    停止划分的条件:
    1. 树的深度达到最大深度
    2. 叶子节点的样本个数小于某个阈值
    3. Gini系数的变化小于某个阈值,也就是变化不大的时候

1、Gini系数

#计算Gini系数
def compute_gini(X,y):
    m = len(X)
    Labelcnt = {}
    
    for j in y:
        if j not in Labelcnt:
            #这里要赋值为1
            Labelcnt[j] = 1
        else:
            Labelcnt[j] += 1
    for key in Labelcnt.keys():
        #计算每个类别的概率
        Labelcnt[key] = (Labelcnt[key] / m) ** 2
    gini = 1 - sum(Labelcnt.values())
    return gini

2、生成左右子树

#按特征分成左右子树,这里的特征取值是连续的,所以采取排序取两数之间的平均值作为划分
#X:样本   index:特征的索引   value:划分的阈值
def split_dataset(X,index,value):
    #存放左右子树
    left_indices = []
    right_indices = []
    
    data = X[:,index]
    for i in range(len(X)):
        if data[i] <= value:
            left_indices.append(i)
        else:
            right_indices.append(i)
    return left_indices,right_indices

3、划分子树后,利用加权平均计算Gini系数

def compute_gini_DA(X,y,node_indices,value):
    left_indices,right_indices = split_dataset(X,node_indices,value)
    x_left,y_left = X[left_indices],y[left_indices]
    x_right,y_right = X[right_indices],y[right_indices]
    
    D = len(X)
    w_left = len(left_indices) / D
    w_right = len(right_indices) / D
    
    gini_left = compute_gini(x_left,y_left)
    gini_right = compute_gini(x_right,y_right)
    
    giniDA = w_left * gini_left + w_right * gini_right
    return giniDA

4、获取最优划分特征

def get_best_split(X,y):
    features = len(X[0])
    #如果特征只有一个,就返回该特征的索引,也就是0
    if features == 1:
        return 0
    #初始化Gini系数和最优特征
    best_gini = 100000
    best_feature = -1
    best_value = -1
    
    for j in range(features):
        feature = sorted(X[:,j])
        #作为该特征下的划分条件备选,保留三位小数
        values = [round((feature[i] + feature[i+1]) / 2,3) for i in range(len(feature) - 1)]
        for value in values:
            gini = compute_gini_DA(X,y,j,value)
            if gini < best_gini:
                best_gini = gini
                best_feature = j
                best_value = value
    return best_feature,best_value

5、建树(递归)

from collections import Counter
def build_tree(X,y,feature,max_depth,cur_depth):
    if max_depth == cur_depth or len(set(y)) == 1:
        return y[0]
    if len(X[0]) == 1:
        collection = Counter(y)
        return collection.most_common(1)[0]
    
    best_findex,best_value = get_best_split(X,y)
    best_feature = feature[best_findex]
    tree = {best_feature:{}}
    
    #递归左右子树
    left_indices,right_indice = split_dataset(X,best_findex,best_value)
    tree[best_feature][best_value] = build_tree(X[left_indices],y[left_indices],feature,max_depth,cur_depth+1)
    tree[best_feature]["other"] = build_tree(X[right_indice],y[right_indice],feature,max_depth,cur_depth+1)
    return tree

feature = ['sepal length','sepal width','petal length','petal width']
decision_tree = build_tree(X_train,Y_train,feature,max_depth=3,cur_depth=0)
print(decision_tree)

3b75dcb6bb1649b19514589393c44abd.png

6、预测结果,同样使用递归找到对应叶子节点所属标签

#预测测试集结果
def classify(decision_tree, features, test):
    # 根节点代表的属性
    first_feature = list(decision_tree.keys())[0]
    second_dict = decision_tree[first_feature]
    index_of_first_feature = features.index(first_feature)

    for key in second_dict.keys():
        # 不等于'others'的key
        if key != "other":
            if test[index_of_first_feature] <= key:
                
                if type(second_dict[key]).__name__ == 'dict':
                    # 需要递归查询
                    classLabel = classify(second_dict[key], features, test)
                # 若当前second_dict的key的value是一个单独的值,返回该标签
                else:
                    classLabel = second_dict[key]
            # 如果测试样本在当前特征的取值不等于key,就说明它在当前特征的取值属于'others'
            else:
                # 如果second_dict['others']的值是个字符串,则直接输出
                if isinstance(second_dict['other'],str):
                    classLabel = second_dict['other']
                # 如果second_dict['others']的值是个字典,则递归查询
                else:
                    classLabel = classify(second_dict['other'], features, test)
    return classLabel

 7、计算准确率和召回率

#预测测试集
labels = []
for i in range(len(x_test)):
    labels.append(classify(decision_tree,feature,X_test[i]))

#利用测试集计算精确率、召回率
#因为是多分类问题,采用宏F1,计算出每个混淆矩阵的精确率和召回率,取平均,再计算F1-score

precision,accuracy,recall = score(Y_test,labels)
precision /= 3
accuracy /= 3
recall /= 3
F1_score = (2 * precision * recall) / (precision + recall)
print(f"精确率:{precision:.3f}")
print(f"准确率:{accuracy:.3f}")
print(f"召回率:{recall:.3f}")
print(f"F1 score:{F1_score:.3f}")

69e296635c1441e0852ae4cc9b229ed7.png

 8、检查是否过拟合

#查看是否过拟合,如果训练集和测试集的得分相差不大,则不会过拟合
def compute_score(labels,y_true):
    cnt = 0
    for i in range(len(labels)):
        if labels[i] == y_true[i]:
            cnt += 1
    return cnt / len(labels)

train_labels = []
for i in range(len(x_train)):
    train_labels.append(classify(decision_tree,feature,X_train[i]))
    
print("训练集上的得分:",compute_score(train_labels,Y_train))
print("测试集上的得分:",compute_score(labels,Y_test))

#运行结果:
#训练集上的得分: 0.9833333333333333
#测试集上的得分: 0.9666666666666667
#结论:未过拟合

以上就是这次期末作业的全部内容,哈哈,有些小累。希望读者能够从文章中取得收获,也欢迎读者的指出不足之处!

选题背景 Titanic生存概率预测是Kaggle平台上的经典竞赛项目,泰坦尼克号成员存活的数据集非常适合新手开始作为机器学习项目和Kaggle竞赛的练手。我们将在该项目中探索具备何种特征的人在此次海滩中幸存的几率更高,并通过改建机器学习模型和深度学习模型来预测乘客的生存率。Titanic项目的描述如下所示: 泰坦尼克号的沉没是历史上最声明远扬的航海事件 1912年4月15日,在她的处女航中,被广泛认为的“沉没” RMS泰坦尼克号与冰山相撞后沉没。 不幸的是,船上没有足够的救生艇供所有人使用,导致2224名乘客和机组人员中的1502人死亡 虽然幸存有一些运气,但似乎有些人比其他人更有可能生存 在这一挑战中,我们要求您建立一个预测模型来回答以下问题:“什么样的人更有可能生存?” 您可以使用乘客数据(即姓名,年龄,性别,社会经济舱等)来进行预测 开发环境 操作系统:Macos Python版本:3.7.4 Anaconda版本:4.9.2 tensorflow版本:2.1.0 keras版本:1.1.0 1 数据源集数据处理 1.1 数据源 数据集来自Kaggle网站提供的数据集,Kaggle提供的乘员数据分为两个部分: 训练集(train.csv) 测试集(test.csv) 训练集将用于构建我们的机器学习/深度学习模型。并且在训练集中对于每位乘客我们会有表示该乘客是否存活的label,也就是survival字段。在训练集中会给出每一位乘客的特征,我们需要从这些乘客的特征当中学得一些知识,或者可以使用特征工程来创建新的特征来帮助模型的构建 测试集数据将用来评判我们模型的表现。因此对于测试集中的每一位乘客没有代表该乘客是否存活的label,我们的模型需要根据每一位乘客的特征值来输出相应的label,真实的label将由kaggle网站保留
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值