Python数据分析案例77——基于机器学习的餐厅评论反欺诈识别

发布于:2025-06-30 ⋅ 阅读:(16) ⋅ 点赞:(0)

背景

基于上一篇的博客研究了图神经网络来进行反欺诈评论的效果预测对比:

Python数据分析案例76——基于图神经网络的餐厅评论反欺诈识别-

 本文需要对上面的同样的数据,同样的训练集和测试集使用普通的机器学习来进行对比,来研究到底是图神经网络的效果好还是普通的机器学习模型效果比较好

数据介绍

该数据集由Dou等人引入,用于增强基于图神经网络的欺诈检测器,以识别伪装欺诈者。数据集包含Yelp评论,具有标签(是否欺诈)和32个归一化特征作为属性,以及评论之间的关系,如共享用户、共享餐厅评级和共享同一月份的餐厅。

数据集详细介绍可以看看我前面这篇博客的介绍。本文因为是构建普通的机器学习流程,大体上只需要使用特征和节点的二维表格数据就够了。

上文已经保存了处理好的数据,训练集和测试集也划分了,就从上文保存下来的数据开始继续做模型对比吧。

当然本文的全部代码文件和数据还是可以从这里获取:餐厅欺诈监测

代码实现

导入包

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import networkx as nx

plt.rcParams['font.sans-serif'] = ['KaiTi']  #指定默认字体 SimHei黑体
plt.rcParams['axes.unicode_minus'] = False   #解决保存图像是负号'

读取数据

df=pd.read_csv('data_split.csv').query("split!='unused' ")#.set_index('txId')
df.head()

第一列是id序号,后面都是特征,最后一列label是标签。

查看非数值数据

df.select_dtypes(exclude=['number']).describe().T

split是我构建训练集和测试集划分出来的标记。

查看缺失值数量

## 缺失值数量
df.isna().sum()[df.isna().sum()!=0]

没有缺失值。

查看标签的分布

df.label.value_counts().plot.pie(figsize=(2,2))

大概15%的黑样本。

训练集,验证集和测试集的比例

df.split.value_counts().plot.barh(figsize=(2,2))

# 边的关系
df_edges=pd.read_csv('./data/net_rur.csv',header=None)
df_edges.columns=['txId1','txId2']
df_edges.shape

再读取一下边的关系查看一下(后续构建新特征使用)

准备数据

直接按照之前的训练集和测试集的划分,准备好x和y

X_train=df.set_index('node_id').query("split=='train' ").drop(columns=['label','split'])
y_train=df.set_index('node_id').query("split=='train' ")['label']

X_test=df.set_index('node_id').query("split=='test' ").drop(columns=['label','split'])
y_test=df.set_index('node_id').query("split=='test' ")['label']

X_train.shape,y_train.shape ,X_test.shape,y_test.shape

3.67w的训练样本,4596条测试集。

机器学习

本文就使用10种普通的机器学习模型

num_classes=2   #2分类问题

导入模型的类

#导包
from sklearn.linear_model import LogisticRegression
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from xgboost.sklearn import XGBClassifier
from lightgbm import LGBMClassifier
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier

实例化模型,装入列表

#逻辑回归
model1 =  LogisticRegression(C=1e10,max_iter=100000)
 
#线性判别分析
model2 = LinearDiscriminantAnalysis()
 
#K近邻
model3 = KNeighborsClassifier(n_neighbors=50)
 
#决策树
model4 = DecisionTreeClassifier(random_state=7,max_features='sqrt',class_weight='balanced')
 
#随机森林
model5= RandomForestClassifier(n_estimators=300,  max_features='sqrt',random_state=7)
 
#梯度提升
model6 = GradientBoostingClassifier(random_state=123)
 
#极端梯度提升
model7 = XGBClassifier(objective='binary:logistic', random_state=1, eval_metric='logloss', use_label_encoder=False)
 
#轻量梯度提升
model8 =LGBMClassifier(objective='binary', random_state=1, verbose=-1)
 
#支持向量机
model9 = SVC(kernel="rbf", random_state=0, probability=True)
 
#神经网络
model10 = MLPClassifier(hidden_layer_sizes=(16,8), random_state=1, max_iter=10000)
 
    
model_list=[model1,model2,model3,model4,model5,model6,model7,model8,model9,model10]
model_name=['逻辑回归','线性判别','K近邻','决策树','随机森林','梯度提升','极端梯度提升','轻量梯度提升','支持向量机','神经网络']
 

#遍历所有的模型,训练,预测,评估 


all_model_pred_proba={}
for i in range(len(model_list)):
    model_C=model_list[i]
    name=model_name[i]
    model_C.fit(X_train, y_train)
    pred_proba=model_C.predict_proba(X_test)
    all_model_pred_proba[name]=pred_proba
    print(f'{name}模型完成')

定义预测的评估函数

from sklearn.metrics import (accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, confusion_matrix)
def evaluation(y_test, y_proba, threshold=0.5):
    """ y_test: 真实标签 (0/1)
        y_proba: 预测概率 (shape=[n_samples,])
        threshold: 分类阈值 (默认0.5)
    """
    # 根据阈值生成预测标签
    y_pred = (y_proba >= threshold).astype(int)
    
    # 计算各项指标
    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred, zero_division=0)
    recall = recall_score(y_test, y_pred, zero_division=0)
    f1 = f1_score(y_test, y_pred, zero_division=0)
    auc = roc_auc_score(y_test, y_proba)  # 注意:直接用概率计算AUC
    
    return accuracy, precision, recall, f1, auc

计算所有模型的评价指标

df_eval=pd.DataFrame(columns=['Accuracy','Precision','Recall','F1_score','AUC'])
for name,pred_proba in all_model_pred_proba.items():
    pred_proba=pred_proba[:,1]
    s=evaluation(y_test,pred_proba, threshold=0.5)
    df_eval.loc[name,:]=list(s)
df_eval

可以看到,模型效果最好的还是树模型,即随机森林,xgboost,lgbm,这三个模型也是常见的效果较好的普通机器学习模型。

现在工业界都是使用lgbm和xgboost模型是有原因的,他们2个的效果在常见的数据集上一般效果是最好的。

现在我们得到了所有的普通机器学习模型的效果,我们可以对比上一篇文章的图神经网络的测试集上的评估效果。:

可以看到,图神经网络的效果和普通的MLP神经网络效果是类似的,图结构并不能带来更好的预测效果,所以这也是图神经网络在工业界运用不多的原因。因为他们构建训练,部署都更麻烦,效果反而还不如普通的机器学习模型,不如树模型LGBM,XGboost。


好,既然图神经网络没有更好的效果,那么这些图结构的数据该怎么使用呢?普通的机器学习就是一个二维的表格,就只要特征跟标签就能做训练,然后图结构的话会在这些样本里面都加入了一些边的连接关系。这些边的连接关系在普通的机器学习模型中是用不到的,但这个边的关系感觉不用的话又可惜,他们毕竟是数据,也能够进行一定的挖掘,如果把它们用上会不会有更好的效果呢?这也是本篇文章接下来的一个目的,即使用这些图结构去用一些算法构建新的特征,然后再将新的特征跟原始的特征合并去训练机器学习模型,看能不能得到更好的效果。

所以本文还要对比一下,把图结构使用随机游走算法构建新的特征是不是能够在原有的特征基础上对模型能够添加更强的,更多的预测性能?


随机游走增强特征

参考了一篇论文,它里面有三种随机游走的方法把样本之间的边的连接关系的图,构造为新的特征:

三种方法的核心区别

DeepWalk

游走策略: 均匀随机游走(完全随机选择邻居) 适用场景: 基础的图结构学习,计算效率高 捕获信息: 图的基本连通性和社区结构

Node2Vec

游走策略: 偏向随机游走,通过p和q参数控制 参数含义:

p: 返回前一个节点的倾向(p越小越容易返回) q: 远离起始节点的倾向(q越小越容易探索远方)

适用场景: 需要在局部探索(DFS-like)和全局探索(BFS-like)间平衡 捕获信息: 更灵活的结构-功能信息

Struc2Vec

游走策略: 基于结构相似性构建新图,然后在结构图上游走 核心思想: 结构相似的节点应该有相似的嵌入(即使在原图中距离很远) 适用场景: 关注节点的结构角色(如"桥梁节点"、"中心节点") 捕获信息: 节点的结构身份和角色。

当然想看论文去复现这些方法是极其困难的,所以说我们只能借助一点AI的力量了,使用了claud,我把文章的描述都丢进去了,然后让他写出了这个构建新特征的类的方法。

这个类很长,我自己也没有仔细看,大体上感觉他的逻辑是对的,并且也能够使用就继续使用了,它的使用很简单,下面有案例的。

写代码嘛,能用就行,除非是很精细的顶尖的算法需求场合才会去研究原理。

from gensim.models import Word2Vec
from node2vec import Node2Vec
import random
from sklearn.preprocessing import StandardScaler

class GraphEmbeddingFeatureEnhancer(object):
    """
    通用图嵌入特征增强器
    支持DeepWalk、Node2Vec、Struc2Vec三种算法
    """
    
    def __init__(self, method='deepwalk', embedding_dim=64, **kwargs):
        """
        参数:
        - method: 'deepwalk', 'node2vec', 'struc2vec'
        - embedding_dim: 嵌入向量维度
        - **kwargs: 各算法的特定参数
        """
        self.method = method.lower()
        self.embedding_dim = embedding_dim
        self.model = None
        self.node_mapping = {}  # 节点ID到图节点的映射
        
        # 设置默认参数
        self.params = {
            'walk_length': kwargs.get('walk_length', 40),
            'num_walks': kwargs.get('num_walks', 10),
            'window_size': kwargs.get('window_size', 5),
            'p': kwargs.get('p', 1),  # Node2Vec参数
            'q': kwargs.get('q', 1),  # Node2Vec参数
            'workers': kwargs.get('workers', 4),
            'epochs': kwargs.get('epochs', 10)
        }
    
    def _build_graph_from_edges(self, edges_df, node_col1='txId1', node_col2='txId2'):
        """
        从边数据框构建NetworkX图
        """
        G = nx.Graph()
        
        # 添加边
        for _, row in edges_df.iterrows():
            node1, node2 = row[node_col1], row[node_col2]
            G.add_edge(node1, node2)
        
        print(f"构建图完成: {G.number_of_nodes()} 个节点, {G.number_of_edges()} 条边")
        return G
    
    def _deepwalk_embedding(self, graph):
        """
        DeepWalk算法实现
        """
        def random_walk(graph, start_node, walk_length):
            walk = [start_node]
            current_node = start_node
            
            for _ in range(walk_length - 1):
                neighbors = list(graph.neighbors(current_node))
                if not neighbors:
                    break
                current_node = random.choice(neighbors)
                walk.append(current_node)
            
            return [str(node) for node in walk]
        
        # 生成随机游走序列
        walks = []
        nodes = list(graph.nodes())
        
        for _ in range(self.params['num_walks']):
            random.shuffle(nodes)
            for node in nodes:
                walk = random_walk(graph, node, self.params['walk_length'])
                walks.append(walk)
        
        # 训练Word2Vec
        model = Word2Vec(
            walks,
            vector_size=self.embedding_dim,
            window=self.params['window_size'],
            min_count=1,
            sg=1,
            workers=self.params['workers'],
            epochs=self.params['epochs']
        )
        
        return model
    
    def _node2vec_embedding(self, graph):
        """
        Node2Vec算法实现
        """
        # 使用node2vec库
        node2vec = Node2Vec(
            graph,
            dimensions=self.embedding_dim,
            walk_length=self.params['walk_length'],
            num_walks=self.params['num_walks'],
            p=self.params['p'],
            q=self.params['q'],
            workers=self.params['workers']
        )
        
        model = node2vec.fit(
            window=self.params['window_size'],
            min_count=1,
            batch_words=4
        )
        
        return model
    
    def _struc2vec_embedding(self, graph):
        """
        Struc2Vec算法简化实现
        注:这里用结构相似性随机游走来近似实现
        """
        def structural_similarity(node1, node2, graph):
            # 简化的结构相似性计算:基于度数和邻居重叠
            deg1, deg2 = graph.degree(node1), graph.degree(node2)
            neighbors1 = set(graph.neighbors(node1))
            neighbors2 = set(graph.neighbors(node2))
            
            # 度数相似性
            deg_sim = 1 / (1 + abs(deg1 - deg2))
            
            # 邻居重叠相似性
            if len(neighbors1) == 0 and len(neighbors2) == 0:
                neighbor_sim = 1.0
            else:
                neighbor_sim = len(neighbors1 & neighbors2) / len(neighbors1 | neighbors2)
            
            return (deg_sim + neighbor_sim) / 2
        
        def structural_random_walk(graph, start_node, walk_length):
            walk = [start_node]
            current_node = start_node
            
            for _ in range(walk_length - 1):
                neighbors = list(graph.neighbors(current_node))
                if not neighbors:
                    break
                
                # 基于结构相似性选择下一个节点
                similarities = []
                for neighbor in neighbors:
                    sim = structural_similarity(current_node, neighbor, graph)
                    similarities.append(sim)
                
                # 按相似性权重随机选择
                if sum(similarities) > 0:
                    probs = np.array(similarities) / sum(similarities)
                    current_node = np.random.choice(neighbors, p=probs)
                else:
                    current_node = random.choice(neighbors)
                
                walk.append(current_node)
            
            return [str(node) for node in walk]
        
        # 生成结构感知的随机游走
        walks = []
        nodes = list(graph.nodes())
        
        for _ in range(self.params['num_walks']):
            random.shuffle(nodes)
            for node in nodes:
                walk = structural_random_walk(graph, node, self.params['walk_length'])
                walks.append(walk)
        
        # 训练Word2Vec
        model = Word2Vec(
            walks,
            vector_size=self.embedding_dim,
            window=self.params['window_size'],
            min_count=1,
            sg=1,
            workers=self.params['workers'],
            epochs=self.params['epochs']
        )
        
        return model
    
    def fit_transform(self, features_df, edges_df, node_id_col='txId', 
                     edge_col1='txId1', edge_col2='txId2', normalize=False):
        """
        核心方法:将特征矩阵从(n,p)扩展为(n,p+q)
        
        参数:
        - features_df: 特征数据框,包含节点ID和特征
        - edges_df: 边数据框,包含边关系
        - node_id_col: 节点ID列名
        - edge_col1, edge_col2: 边的两个节点列名
        - normalize: 是否标准化特征
        
        返回:
        - enhanced_df: 增强后的特征数据框
        """
        print(f"使用 {self.method.upper()} 算法构建图嵌入特征...")
        
        # 1. 构建图
        graph = self._build_graph_from_edges(edges_df, edge_col1, edge_col2)
        
        # 2. 选择算法生成嵌入
        if self.method == 'deepwalk':
            self.model = self._deepwalk_embedding(graph)
        elif self.method == 'node2vec':
            self.model = self._node2vec_embedding(graph)
        elif self.method == 'struc2vec':
            self.model = self._struc2vec_embedding(graph)
        else:
            raise ValueError(f"不支持的方法: {self.method}")
        
        # 3. 获取节点嵌入
        node_ids = features_df[node_id_col].to_numpy()
        embeddings = []
        
        for node_id in node_ids:
            try:
                embedding = self.model.wv[str(node_id)]
                embeddings.append(embedding)
            except KeyError:
                # 如果节点不在图中,用零向量填充
                embeddings.append(np.zeros(self.embedding_dim))
                print(f"警告: 节点 {node_id} 不在图中,使用零向量")
        
        embeddings = np.array(embeddings)
        print(f"生成图嵌入特征: {embeddings.shape}")
        
        # 4. 特征拼接
        # 提取原始特征(排除ID和标签列)
        feature_cols = [col for col in features_df.columns 
                       if col not in []]   #node_id_col, 'label',
        original_features = features_df[feature_cols].to_numpy()
        
        print(f"原始特征维度: {original_features.shape}")
        
        # 标准化(可选)
        if normalize:
            scaler_orig = StandardScaler()
            scaler_emb = StandardScaler()
            original_features = scaler_orig.fit_transform(original_features)
            embeddings = scaler_emb.fit_transform(embeddings)
        
        # 拼接特征
        enhanced_features = np.concatenate([original_features, embeddings], axis=1)
        print(f"增强后特征维度: {enhanced_features.shape}")
        
        # 5. 构建返回的数据框
        #enhanced_df = features_df.copy()
        
        # 更新特征列
        # 一次性更新所有特征列和图嵌入列
        new_data = {
            **{col: enhanced_features[:, i] for i, col in enumerate(feature_cols)},
            **{f'{self.method}_emb_{i}': enhanced_features[:, len(feature_cols) + i] 
               for i in range(self.embedding_dim)}
        }
        
        enhanced_df = pd.DataFrame(new_data)
        
        print(f"✅ 特征增强完成!从 {len(feature_cols)} 维扩展到 {enhanced_features.shape[1]} 维")
        
        return enhanced_df

下面是使用案例,当然,这里就不运行了。

# # 使用演示
# def demo_usage():
#     """
#     演示如何使用GraphEmbeddingFeatureEnhancer
#     """
#     print("=" * 60)
#     print("图嵌入特征增强器使用演示")
#     print("=" * 60)
    
#     # 1. 创建模拟数据
#     print("1. 创建模拟数据...")
    
#     # 特征数据框
#     np.random.seed(42)
#     n_samples = 1000
#     df = pd.DataFrame({
#         'txId': range(n_samples),
#         'amount': np.random.exponential(1000, n_samples),
#         'balance': np.random.exponential(5000, n_samples), 
#         'age': np.random.randint(18, 80, n_samples),
#         'credit_score': np.random.randint(300, 850, n_samples),
#         'label': np.random.choice([0, 1], n_samples, p=[0.9, 0.1])
#     })
    
#     # 边数据框
#     n_edges = 3000
#     df_edges = pd.DataFrame({
#         'txId1': np.random.choice(n_samples, n_edges),
#         'txId2': np.random.choice(n_samples, n_edges)
#     })
#     # 移除自环
#     df_edges = df_edges[df_edges['txId1'] != df_edges['txId2']].drop_duplicates()
    
#     print(f"   特征数据: {df.shape}")
#     print(f"   边数据: {df_edges.shape}")
    
#     # 2. 使用不同算法进行特征增强
#     methods = ['deepwalk', 'node2vec', 'struc2vec']
    
#     for method in methods:
#         print(f"\n2.{methods.index(method)+1} 使用 {method.upper()} 算法...")
        
#         enhancer = GraphEmbeddingFeatureEnhancer(
#             method=method,
#             embedding_dim=32,  # 为了演示,设置较小的维度
#             walk_length=20,
#             num_walks=5
#         )
        
#         # 核心调用:特征增强
#         enhanced_df = enhancer.fit_transform(df, df_edges)
        
#         print(f"   增强前: {df.shape}")
#         print(f"   增强后: {enhanced_df.shape}")
#         print(f"   新增特征: {enhanced_df.shape[1] - df.shape[1]} 维")
        
#         # 显示部分增强后的特征
#         emb_cols = [col for col in enhanced_df.columns if f'{method}_emb' in col]
#         print(f"   图嵌入特征列: {emb_cols[:3]}...")
# #demo_usage()

然后对我们上面的数据进行新特征的构建,特征增强

# 你的数据准备
# df: 包含txId, label和其他特征的数据框  
# df_edges: 包含txId1, txId2的边数据框

# 一行代码完成特征增强
enhancer = GraphEmbeddingFeatureEnhancer(method='deepwalk', embedding_dim=16)
enhanced_df = enhancer.fit_transform(df, df_edges, node_id_col='node_id', 
                     edge_col1='txId1', edge_col2='txId2', normalize=False)

因为这里面有的样本是没有任何边的连接的,所以说就不在图里面的样本直接使用零向量填充就好了。

然后在使用另外一种随机游走的方法去构建性特征。

enhancer = GraphEmbeddingFeatureEnhancer(method='node2vec', embedding_dim=32)
enhanced_df2 = enhancer.fit_transform(df, df_edges, node_id_col='node_id', 
                     edge_col1='txId1', edge_col2='txId2', normalize=False)

还有第三种随机有多的方法构建新特征

enhancer = GraphEmbeddingFeatureEnhancer(method='struc2vec', embedding_dim=32)
enhanced_df3 = enhancer.fit_transform(df, df_edges, node_id_col='node_id',
                     edge_col1='txId1', edge_col2='txId2', normalize=False)

现在我们就得到了三个新的数据框,也就是三个在原来特征上用不同的方法构建出的新的特征的数据框,我们用这新的特征去再和标签做机器学习模型去对比,其测试集上的效果。

当然由于前面我们知道了只有随机森林,xgboost,lgbm这三个模型效果比较好,所以我们下面就不做那么多的机器学习模型,就只做这三个机器学习模型了。


新加入特征后效果预测评估

deepwalk

定义划分训练测试数据的函数

def split_df_check(df,or_df_label):
    df=df.merge(or_df_label[['label']],  left_index=True,right_index=True)
    X_train=df.query("split=='train' ").drop(columns=['label','split']).copy().astype('float')
    y_train=df.query("split=='train' ")['label'].copy().astype('float')

    X_test=df.query("split=='test' ").drop(columns=['label','split']).copy().astype('float')
    y_test=df.query("split=='test' ")['label'].copy().astype('float')

    print(X_train.shape,y_train.shape ,X_test.shape,y_test.shape)
    return X_train,y_train ,X_test,y_test
X_train,y_train ,X_test,y_test=split_df_check(enhanced_df,df)

定义模型的函数

model_name=['随机森林','极端梯度提升','轻量梯度提升']
def get_all_model():
    #随机森林
    model1= RandomForestClassifier(n_estimators=300,  max_features='sqrt',random_state=7)

    #极端梯度提升
    model2 = XGBClassifier(objective='binary:logistic', random_state=1, eval_metric='logloss', use_label_encoder=False)

    #轻量梯度提升
    model3 =LGBMClassifier(objective='binary', random_state=1, verbose=-1)
    model_list=[model1,model2,model3]

    return model_list
model_list=get_all_model()

模型训练,评估 

#遍历所有的模型,训练,预测,评估
def eval_all_model(model_list):
    all_model_pred_proba={}
    for i in range(len(model_list)):
        model_C=model_list[i]
        name=model_name[i]
        model_C.fit(X_train, y_train)
        pred_proba=model_C.predict_proba(X_test)
        all_model_pred_proba[name]=pred_proba
        print(f'{name}模型完成')
    df_eval1=pd.DataFrame(columns=['Accuracy','Precision','Recall','F1_score','AUC'])
    for name,pred_proba in all_model_pred_proba.items():
        pred_proba=pred_proba[:,1]
        s=evaluation(y_test,pred_proba, threshold=0.41)
        df_eval1.loc[name,:]=list(s)
    return df_eval1
eval_all_model(model_list)

我们可以看到,之前就使用原始特征的时候,AUC最高的XGBoost是0.95,现在升到0.96,说明还是有一定的提升的!!! 也就是说这种随机游走算法从图结构里面构建新特征的数据挖掘方法是有效的。

变量重要性

既然这些特征是有效的,那我们就看一下这个特征跟原始特征相比的话到底重不重要?我们就要比较一下模型的重要变量性排序。

model= XGBClassifier(objective='binary:logistic', random_state=1, eval_metric='logloss', use_label_encoder=False)
model.fit(X_train, y_train)
model.score(X_test, y_test)

画出特征重要性的柱状图可视化 

sorted_index = model.feature_importances_.argsort()[::-1]
plt.figure(figsize=(7, 16),dpi=128) # 可以调整尺寸以适应所有特征
 
# 使用 seaborn 来绘制条形图
sns.barplot(x=model.feature_importances_[sorted_index], y=X_train.columns[sorted_index], orient='h')
plt.xlabel('Feature Importance')  # x轴标签
plt.ylabel('Feature')             # y轴标签
plt.show()

变量比较多,就不展示那么多了,我们可以看到deepwalk生成的新的嵌入型向量特征其中有一个居然排在了最前面!! 也就是说他对模型预测贡献能力显著,并且在排名前中部的区域也出现了很多随机游走生成了新的图结构特征,也就是说这些新生的特征还是比较重要的。

再对比一下其他两种随机游走的方法生成的新特征的模型的效果。

node2vec
X_train,y_train ,X_test,y_test=split_df_check(enhanced_df2,df)
model_list=get_all_model()
eval_all_model(model_list)

可以看到有一点点轻微的提升,但是相比出第一种方法来说的话,提升有限,xgboost的AUC最高只有0.958。

struc2vec
X_train,y_train ,X_test,y_test=split_df_check(enhanced_df3,df)
model_list=get_all_model()
eval_all_model(model_list)

可以看到提升效果也还不错,xgboost的AUC最高有0.962。F1值0.81,比第一种方法还高一点。


总结

本文对比了普通的机器学习模型,常见的树模型相比于图神经网络的效果,发现图神经网络的效果只能和最普通的多层感知机神经网络mlp结构的效果是类似的,图结构特征并不能带来更好的提升,在表格数据里面表现最好的还是树模型(xgboost,lgbm)。

然后又研究了这些图结构该怎么使用,发现使用随机游走算法去增强这些数据特征,构造新的特征,再去使用数模型进行训练,能够让准确率有一定的提升。但是提升来说还是比较有限,而且由于这些随机游走方法不够成熟,是使用一些第三方库实现的计算效率很慢,如此高昂的计算成本和时间换来的效果提升微乎其微,可能也不是很值得?

我还对比了之前案例里面的比特币反欺诈交易的图结构的数据,发现也是一样的结论。

当然具体场景一下可能会有更高的提升,这个也要再研究。

创作不易,看官觉得写得还不错的话点个关注和赞吧,本人会持续更新python数据分析领域的代码文章~(需要定制类似的代码可私信)