机器学习Day04(上)

1. 如何预测客户是否流失?(留存预估)

        拉新

        留存

        流失 VS 留存?

        参考用户的一些历史(业务)行为

                人口统计学信息(user表中):年龄、性别、学历、婚姻……

                业务信息:购买记录、浏览记录……

先去发育一段时间,搞一些原始的累积

历史数据:大量留存的用户 + 大量流失的用户

有了这些历史数据,才能谈优化发展。

预测:二分类即可

        根据历史数据,做二分类预测

2. 数据存在的一些疑惑?

拿到数据后,无法判定哪些特征有用,哪些特征没用。

        那就都先留着。

对于离散型变量,都是非数字的标准格式。

        状态编码

                zero index

                        0 ~ N-1

                        不会增加特征的维度

                        但是会潜在引入状态大小问题

                        本质上:把离散变量当做连续变量处理了。

                one hot

                        state 0: [1,0,0,0,……,0]

                        state 1: [0,1,0,0,……,0]

                        state N: [0,0,0,0,……,1]

                        一个特征变成N个特征

                        构建了一个稀疏矩阵(大量的0,少数的有用数据)

                        浪费了存储和计算

                        特征之间距离相等,互相垂直

连续型变量:

        数据级不同,量纲不同

1. 数据读取

"""
1. 数据读取
"""
#导入Pandas库,并将其简称为pd。
#Pandas是一个强大的Python数据分析库,提供了大量的数据处理和分析的工具。
import pandas as pd

#使用Pandas的read_csv函数来读取位于/Users/csblog/信用卡客户流失数据集.csv的CSV文件。
#读取的数据被存储在变量data中,data现在是一个DataFrame对象,
#DataFrame是Pandas中用于存储和操作结构化数据的主要数据结构。
data = pd.read_csv(filepath_or_buffer="/Users/csblog/信用卡客户流失数据集.csv")

#打印出data DataFrame的形状,即它的行数和列数。
#形状以元组(行数, 列数)的形式给出。这对于快速了解数据集的大小非常有用。
data.shape

#打印出data DataFrame的简要摘要,
#包括每列的名称、非空值的数量、数据类型(如整型、浮点型、对象型等),以及内存使用情况。
#这对于了解数据集的基本结构和数据类型的分布很有帮助。
data.info()

#返回data DataFrame的最后3行数据(n=3指定了返回的行数)。
#这对于查看数据集的末尾部分,了解数据集的结束情况或查看最新记录非常有用。
data.tail(n=3)

#从data DataFrame中随机抽取3行数据。
#这对于快速查看数据集的样本或进行随机抽样分析很有用。
#默认情况下,sample方法会返回随机抽取的行,
#但你也可以通过random_state参数指定一个随机种子来确保结果的可重复性。
data.sample(n=3)

#注意:虽然data.tail(n=3)和data.sample(n=3)都返回了数据框的几行,
#但它们的目的不同。tail返回的是数据集末尾的几行,而sample返回的是随机选取的几行。



"""
2. 数据清洗
重复 什么叫重复?user_id? 缺失 异常
"""
# 判断是否有重复
data.duplicated(subset=["CLIENTNUM"]).sum()

#删除重复(保留其中一条) 
data.drop_duplicates(subset=["CLIENTNUM"],inplace=True)

data.info()


#用于检查DataFrame data中每一列(或每一行,如果你对行进行操作的话)中缺失值(NaN,即“Not a Number”)的数量
data.isna().sum()


#在Pandas中,data.dropna(how="any", inplace=True)
#是一个用于删除DataFrame data中包含缺失值(NaN)的行或列的操作。
#这里的参数具体说明了如何执行这个删除操作:
#how="any":这个参数指定了当任何一个元素(在行或列中,取决于axis参数,但默认情况下axis=0表示行)#是缺失值时,整行(或列,如果指定了axis=1)就会被删除。
#在这个例子中,因为how="any",所以任何包含至少一个NaN的行都会被删除。
#inplace=True:这个参数指定了操作是否应该直接在原始DataFrame上进行,
#而不是返回一个新的、修改后的DataFrame副本。
#当inplace=True时,原始DataFrame data会被修改,并且这个操作不会返回任何值(即返回None)。
#如果你不想修改原始DataFrame,可以省略这个参数或将其设置为False,这样操作就会返回一个新的#DataFrame副本,而原始DataFrame保持不变。
#因此,data.dropna(how="any", inplace=True)会删除data中所有包含至少一个NaN值的行,并且这个修改会直接反映在原始的data DataFrame上。
#注意:如果你只想删除包含全部NaN值的行(或列),你可以将how参数设置为"all"。但在大多数情况下,#how="any"是删除包含至少一个NaN值的行的常用选择。


#删除CLIENTNUM列
data.drop(columns="CLIENTNUM",inplace=True)

"""
 3. 数据预处理
        编码问题
        异常问题
        量纲问题
"""
# 判断Attrition_Flag有几个离散状态
data["Attrition_Flag"].unique()

#构建状态字典
#字典推导式,该字典的键(key)是Attrition_Flag列中的唯一值,
#而值(value)是这些唯一值在枚举中的索引(从0开始)。
#注意:enumerate() 非常适合用于在 for 循环中同时获取索引和值。
#enumerate() 函数返回一个枚举对象,该对象是一个迭代器,它生成由 (index, value) 组成的元组
attrition_dict = {attrition:idx for idx, attrition in enumerate(data["Attrition_Flag"].unique())}


#更新对应的列
data["Attrition_Flag"]=data["Attrition_Flag"].apply(func=lambda ele:attrition_dict[ele])

idx2label = {idx:attrition for attrition,idx in attrition_dict.items()}
"""
3.2, Customer_Age
按位或 |
按位与 &
否定,按位取反 !
"""

#判断有多少个年龄小于0或是大于150岁的异常数据
((data["Customer_Age"] < 0) | (data["Customer_Age"]>150)).sum()
"""
3.3, Gender
"""
# 判断有多少个离散状态
data["Gender"].unique()
#构建状态字典
gender_dict = {gender:idx for idx,gender in enumerate(data["Gender"].unique())}
#更新对应的列
data["Gender"] = data["Gender"].apply(func = lambda ele:gender_dict[ele])
"""
3.5, Education_Level
"""
#判断有多少个离散状态
data["Education_Level"].unique()
#构建状态字典
education_dict = {education:idx for idx,education in enumerate(data["Education_Level"].unique())}
#更新对应列
data["Education_Level"] = data["Education_Level"].apply(func=lambda ele:education_dict[ele])
"""
4,数据拆分
"""
y = data["Attrition_Flag"].to_numpy()
X = data.drop(columns = ["Attrition_Flag"]).to_numpy()

X.shape,y.shape

from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=0)

X_train.shape
y_train.shape
"""
5, 数据标准化处理
"""
#从训练集中抽取预处理参数mu和sigma
#计算数组X沿着指定轴的均值
#axis=0表示沿着数组的第一个轴(通常是行)进行操作,即计算每一列的平均值。
#对于二维数组(矩阵)来说,axis=0会将所有行的对应元素相加,然后除以行数,得到每一列的均值。
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)

#执行标准化操作
X_train = (X_train - mu)/sigma
X_test = (X_test - mu)/sigma
"""
6, 数据保存
    特征编码时的状态字典
    特征的各种异常判断
    标准化处理的参数

"""
import joblib

state_dict = [idx2label, gender_dict, education_dict, marital_dict, income_dict, card_dict, mu, sigma]
data = [X_train, y_train, X_test, y_test]

joblib.dump(value=[state_dict, data], filename="all_data.csblog")

2. 建模预测

"""
1. 数据加载

"""
import joblib

[state_dict,data]=joblib.load(filename="all_data.csblog")

idx2label, gender_dict, education_dict, marital_dict, income_dict, card_dict, mu, sigma = state_dict
X_train, y_train, X_test, y_test = data

X_train.shape


"""
2. 尝试所有算法
    二分类问题:
    算法:
        KNN
        朴素贝叶斯
        决策树   
"""

"""
2.1 KNN算法
"""

# 从sklearn的neighbors模块中导入KNeighborsClassifier类,用于实现K最近邻算法。
from sklearn.neighbors import KNeighborsClassifier

# 初始化相关参数
best_n_neighbor = 0
best_acc = 0
best_model = None
# 简单遍历,寻找比较好的 N
for n_neighbor in range(3, 31, 1):
# 创建KNN分类器实例
#n_neighbors=5指定了在预测时使用最近的5个邻居
    knn = KNeighborsClassifier(n_neighbors=n_neighbor)
# 使用训练数据训练模型 
    knn.fit(X=X_train, y=y_train)
# 使用训练好的模型对测试集进行预测
    y_pred = knn.predict(X=X_test)
    acc = (y_pred == y_test).mean()
    if acc > best_acc:
        best_n_neighbor = n_neighbor
        best_acc = acc
        best_model = knn
        print(f"找到一个更好的模型: {best_n_neighbor}, {best_acc}")
# 保存模型
joblib.dump(value=[best_n_neighbor, best_acc, best_model], filename="best_knn.csblog")



"""
2.2 朴素贝叶斯
    高斯朴素贝叶斯
    高斯:假定所有的特征都是连续型的
"""
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
gnb.fit(X=X_train, y=y_train)
y_pred = gnb.predict(X=X_test)
acc = (y_pred == y_test).mean()
acc


"""
2.3 决策树
"""
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
dtc.fit(X=X_train, y=y_train)
y_pred = dtc.predict(X=X_test)
acc = (y_pred == y_test).mean()
acc

3. 推理过程

        从原始数据开始推理

x = "709967358,48,M,4,Post-Graduate,Single,$80K - $120K,Blue,36,6,2,3,30367,2362,28005,1.708,1671,27,0.929,0.078"

def predict(x):
    """
        推理函数
    """
    import numpy as np
    # print(f"原始输入:{x}")
    x = x.split(",")[1:]
    # print(f"切分之后:{x}")
    temp = []
    # 1, age
    temp.append(float(x[0]))
    # 2, gender
    temp.append(gender_dict[x[1]])
    # 3, 
    temp.append(float(x[2]))
    # 4, 
    temp.append(education_dict[x[3]])

    # 5, 
    temp.append(marital_dict[x[4]])
    # 6,
    temp.append(income_dict[x[5]])

    # 7, 
    temp.append(card_dict[x[6]])

    # 8 - 19
    temp.extend([float(ele) for ele in x[7: ]])
    
    # print(f"编码之后:{temp}")

    # 标准化之后
    x = np.array(temp)
    x = (x - mu) / sigma
    # print(f"标准化之后:{x}")

    # 模型推理

    y_pred = dtc.predict(X=[x])
    # print(f"推理结果:{y_pred}")

    # 结果解析
    final_result = idx2label[y_pred[0]]
    # print(f"最终结果:{final_result}")
    return final_result   

服务端 C/S 架构部署

  • FastAPI web

4. 扩展

        评价指标

                准确率 Accuracy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值