「エンティティ解決とグラフニューラルネットワークを用いた詐欺検知」

Entity resolution and fraud detection using graph neural networks

エンティティ解決が不正検出の機械学習を改善する実践的なガイド

グラフニューラルネットワークの表現(Bing Image Creatorを使用して作成された画像)

オンライン詐欺は、金融、電子商取引、その他関連産業にとってますます深刻な問題です。この脅威に対応するため、組織は機械学習と行動解析に基づく詐欺検出メカニズムを使用しています。これらの技術により、異常なパターン、異常な行動、および不正活動をリアルタイムで検出することができます。

残念ながら、しばしば現在の取引(例:注文)のみを考慮に入れるか、プロセスが顧客のプロファイルからの歴史的データに基づいている場合があります。このプロファイルは顧客IDによって識別されます。しかし、プロの詐欺師は、プロファイルの肯定的なイメージを構築するために低価値の取引を使用して顧客プロファイルを作成するかもしれません。さらに、彼らは同時に複数の類似のプロファイルを作成するかもしれません。詐欺が発生した後、攻撃された企業はこれらの顧客プロファイルが互いに関連していたことに気付くのです。

エンティティ解決を使用すると、異なる顧客のプロファイルを簡単に組み合わせて、単一の360°顧客ビューを作成することができます。これにより、すべての過去の取引の全体像を把握することができます。このデータを機械学習で使用することで、ニューラルネットワークや単純な線形回帰を使用することで、結果のモデルに追加の価値が提供されますが、個々の取引がどのように関連しているかも考慮することが本当の価値を生み出します。これがグラフニューラルネットワーク(GNN)が登場する場所です。取引記録から抽出された特徴だけでなく、取引がどのようにリンクされているか(グラフエッジから生成された特徴)やエンティティグラフの一般的なレイアウトを見ることもできます。

例データ

詳細については、ここで免責事項を述べます:私は開発者であり、エンティティ解決の専門家であり、データサイエンティストやMLの専門家ではありません。一般的なアプローチは正しいと考えていますが、ベストプラクティスに従っているわけではありませんし、隠れたノードの数などの特定の側面を説明することもできません。この記事をインスピレーションとし、GNNのレイアウトや設定についてはご自身の経験を活かしてください。

この記事の目的は、エンティティグラフのレイアウトから得られる洞察に焦点を当てることです。この目的のために、エンティティを生成する小さなGolangスクリプトを作成しました。各エンティティは不正または非不正のいずれかのラベルが付けられ、レコード(注文)とエッジ(これらの注文がどのようにリンクされているか)で構成されています。次に示す単一のエンティティの例を参照してください:

{  "fraud":1,  "records":[    {      "id":0,      "totalValue":85,      "items":2    },    {      "id":1,      "totalValue":31,      "items":4    },    {      "id":2,      "totalValue":20,      "items":9    }  ],  "edges":[    {      "a":1,      "b":0,      "R1":1,      "R2":1    },    {      "a":2,      "b":1,      "R1":0,      "R2":1    }  ]}

各レコードには2つの(潜在的な)特徴があります。合計金額と購入アイテム数です。ただし、生成スクリプトはこれらの値を完全にランダム化しているため、詐欺ラベルを推測する際には価値を提供しないはずです。各エッジにはR1とR2の2つの特徴も付属しています。これらは、例えば2つのレコードAとBが似た名前と住所(R1)または似たメールアドレス(R2)を介してリンクされているかどうかを示すものです。さらに、この例では関連性のない属性(名前、住所、メールアドレス、電話番号など)を意図的に省略しましたが、エンティティ解決プロセスには通常関係がある属性です。R1とR2もランダム化されているため、GNNにとっても価値を提供しません。ただし、詐欺ラベルに基づいて、エッジは2つの異なる方法でレイアウトされます:スターライクレイアウト(fraud=0)またはランダムレイアウト(fraud=1)。

アイデアは、不正でない顧客は通常、正確な一致する関連データを提供する可能性が高いため、同じ住所や同じ名前など、つづりのエラーが少ない場合があります。したがって、新しいトランザクションは重複として認識される可能性があります。

重複排除されたエンティティ(著者による画像)

詐欺顧客は、さまざまな名前や住所を使用して、コンピュータの背後にまだ同じ人物であるという事実を隠したい場合があります。しかし、エンティティ解決ツールは依然として類似性を認識するかもしれません(例:地理的および時間的な類似性、メールアドレスの繰り返しパターン、デバイスIDなど)、ただし、エンティティグラフはより複雑に見えるかもしれません。

複雑であり、おそらく詐欺的なエンティティ(著者による画像)

少し簡単ではないようにするために、生成スクリプトには5%のエラー率もあります。つまり、星のようなレイアウトの場合には詐欺としてラベル付けされ、ランダムなレイアウトの場合には非詐欺としてラベル付けされます。また、データが実際のレイアウトを決定するのに十分ではない場合もあります(例:1つまたは2つのレコードのみ)。

{  "fraud":1,  "records":[    {      "id":0,      "totalValue":85,      "items":5    }  ],  "edges":[      ]}

実際には、レコード属性、エッジ属性、およびエッジレイアウトの3つの種類の特徴から貴重な洞察を得ることができるでしょう。以下のコード例ではこれを考慮しますが、生成されたデータには含まれていません。

データセットの作成

この例では、python(データ生成以外)とDGLを使用しています。pytorchバックエンドも使用しています。完全なJupyterノートブック、データ、および生成スクリプトは、GitHubで見つけることができます。

まず、データセットをインポートしましょう:

import osos.environ["DGLBACKEND"] = "pytorch"import pandas as pdimport torchimport dglfrom dgl.data import DGLDatasetclass EntitiesDataset(DGLDataset):    def __init__(self, entitiesFile):        self.entitiesFile = entitiesFile        super().__init__(name="entities")    def process(self):        entities = pd.read_json(self.entitiesFile, lines=1)        self.graphs = []        self.labels = []        for _, entity in entities.iterrows():            a = []            b = []            r1_feat = []            r2_feat = []            for edge in entity["edges"]:                a.append(edge["a"])                b.append(edge["b"])                r1_feat.append(edge["R1"])                r2_feat.append(edge["R2"])            a = torch.LongTensor(a)            b = torch.LongTensor(b)            edge_features = torch.LongTensor([r1_feat, r2_feat]).t()            node_feat = [[node["totalValue"], node["items"]] for node in entity["records"]]            node_features = torch.tensor(node_feat)            g = dgl.graph((a, b), num_nodes=len(entity["records"]))            g.edata["feat"] = edge_features            g.ndata["feat"] = node_features            g = dgl.add_self_loop(g)            self.graphs.append(g)            self.labels.append(entity["fraud"])        self.labels = torch.LongTensor(self.labels)    def __getitem__(self, i):        return self.graphs[i], self.labels[i]    def __len__(self):        return len(self.graphs)dataset = EntitiesDataset("./entities.jsonl")print(dataset)print(dataset[0])

これにより、エンティティファイル(JSON行ファイル)が処理されます。各行は単一のエンティティを表します。各エンティティを反復処理する間に、エッジ特徴(形状が[e、2]のロングテンソル、e =エッジの数)とノード特徴(形状が[n、2]のロングテンソル、n =ノードの数)が生成されます。それから、aとbに基づいてグラフを構築し(形状が[e、1]のそれぞれのロングテンソル)、エッジとグラフの特徴をそのグラフに割り当てます。すべての結果のグラフはデータセットに追加されます。

モデルアーキテクチャ

データが準備できたので、GNNのアーキテクチャについて考える必要があります。以下は私が考案したものですが、実際のニーズにより適応できるかもしれません:

import torch.nn as nnimport torch.nn.functional as Ffrom dgl.nn import NNConv, SAGEConvclass EntityGraphModule(nn.Module):    def __init__(self, node_in_feats, edge_in_feats, h_feats, num_classes):        super(EntityGraphModule, self).__init__()        lin = nn.Linear(edge_in_feats, node_in_feats * h_feats)        edge_func = lambda e_feat: lin(e_feat)        self.conv1 = NNConv(node_in_feats, h_feats, edge_func)        self.conv2 = SAGEConv(h_feats, num_classes, "pool")    def forward(self, g, node_features, edge_features):        h = self.conv1(g, node_features, edge_features)        h = F.relu(h)        h = self.conv2(g, h)        g.ndata["h"] = h        return dgl.mean_nodes(g, "h")

コンストラクタは、ノード特徴の数、エッジ特徴の数、隠れ層のノード数、およびラベル(クラス)の数を受け取ります。それから、2つのレイヤーを作成します。1つ目は、エッジとノードの特徴に基づいて隠れたノードを計算するNNConvレイヤーで、2つ目は、隠れたノードに基づいて結果のラベルを計算するGraphSAGEレイヤーです。

トレーニングとテスト

もう少しです。次に、トレーニングとテストのためのデータを準備します。

from torch.utils.data.sampler import SubsetRandomSamplerfrom dgl.dataloading import GraphDataLoadernum_examples = len(dataset)num_train = int(num_examples * 0.8)train_sampler = SubsetRandomSampler(torch.arange(num_train))test_sampler = SubsetRandomSampler(torch.arange(num_train, num_examples))train_dataloader = GraphDataLoader(    dataset, sampler=train_sampler, batch_size=5, drop_last=False)test_dataloader = GraphDataLoader(    dataset, sampler=test_sampler, batch_size=5, drop_last=False)

ランダムサンプリングを使用して、80/20の割合でデータを分割し、それぞれのサンプルに対してデータローダーを作成します。

最後のステップは、モデルをデータで初期化し、トレーニングを実行した後に結果をテストすることです。

h_feats = 64learn_iterations = 50learn_rate = 0.01model = EntityGraphModule(    dataset.graphs[0].ndata["feat"].shape[1],    dataset.graphs[0].edata["feat"].shape[1],    h_feats,    dataset.labels.max().item() + 1)optimizer = torch.optim.Adam(model.parameters(), lr=learn_rate)for _ in range(learn_iterations):    for batched_graph, labels in train_dataloader:        pred = model(batched_graph, batched_graph.ndata["feat"].float(), batched_graph.edata["feat"].float())        loss = F.cross_entropy(pred, labels)        optimizer.zero_grad()        loss.backward()        optimizer.step()num_correct = 0num_tests = 0for batched_graph, labels in test_dataloader:    pred = model(batched_graph, batched_graph.ndata["feat"].float(), batched_graph.edata["feat"].float())    num_correct += (pred.argmax(1) == labels).sum().item()    num_tests += len(labels)acc = num_correct / num_testsprint("テストの正解率:", acc)

ノードとエッジの特徴のサイズ(この例では両方とも2)、隠れ層のノード(64)、およびラベルの数(詐欺かどうかであるため2)を提供してモデルを初期化します。最適化手法は学習率0.01で初期化されます。その後、合計50回のトレーニングイテレーションを実行します。トレーニングが完了したら、テストデータローダーを使用して結果をテストし、正解率を表示します。

さまざまな実行で、典型的な正解率は70%から85%の範囲でした。ただし、一部の例外では55%まで低下しました。

結論

例のデータセットから利用可能な情報は、ノードがどのように接続されているかの説明のみですが、初期結果は非常に有望であり、実世界のデータとより多くのトレーニングを使用すれば高い正解率が可能であることを示唆しています。

実際のデータを扱う場合、レイアウトは一貫しておらず、レイアウトと詐欺行為との明白な相関関係は提供されません。したがって、エッジとノードの特徴も考慮する必要があります。この記事からの重要なポイントは、エンティティ解決がグラフニューラルネットワークを使用した詐欺検出に理想的なデータを提供し、詐欺検出エンジニアのツールセットの一部として考慮すべきであるということです。

元の記事はhttps://tilores.ioで公開されています。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more