CoolFace
Modelpublic

thealper2/graphcodebert-code-clone-detection

sourceHugging Facemitupdated 1d agoView on Hugging Face
0likes11downloads
Model Card

graphcodebert-code-clone-detection

Binary code-clone detection. Full fine-tune of `microsoft/graphcodebert-base` on `PoolC/1-fold-clone-detection-600k-5fold`, using GraphCodeBERT's data-flow-aware pairwise architecture.

Output labels: 0 = not clone, 1 = clone.

Architecture

Not a generic sequence-pair classifier. The two snippets are encoded separately by one shared GraphCodeBERT encoder, each with its own graph-guided masked attention, and the two <s> vectors are concatenated for classification:

Linear(2 x 768 -> 768) -> tanh -> Linear(768 -> 2)

Per-snippet input layout (length 640):

segmentlengthcontent`position_idx`
code tokens512<s> + BPE code tokens + </s>2 .. n+1
data-flow nodes128one slot per DFG variable node (<unk> id)0
paddingremainder<pad>1

A data-flow node's input embedding is the average of the embeddings of the code tokens it was identified from. Graph-guided attention allows: code to code; <s>/</s> to everything; node to the code tokens it comes from (and back); node to adjacent nodes.

Preprocessing

The dataset contains Python snippets, so data flow is extracted with the tree-sitter-python grammar via a port of GraphCodeBERT's DFG_python extractor (comment/docstring stripping -> AST -> variable states -> comesFrom / computedFrom edges).

code_length512
data_flow_length128
total sequence length640
distinct snippets featurised44,950
mean data-flow nodes / snippet44.22
snippets with empty data flow263
total data-flow edges2,481,388
extraction status counts{"ok": 44930, "comment_strip_failed": 13, "dfg_failed": 7}

No example was dropped: a snippet whose data flow could not be extracted is kept with an empty graph and counted above.

Data splits

The repository provides one of 5 predefined folds as train + val; those groups are disjoint and are kept as-is. val is partitioned further into validation/test along problem-group boundaries.

similar equals (code1_group == code2_group) for every row, so the group columns are a perfect label proxy and are never used as features.

splitsourcepairspositivesnegativesgroups
traintrain fold50,00025,00025,000240
validationhalf of val by group20,00010,00010,00029
testother half of val by group20,00010,00010,00030

Train/validation/test share no problem group and no code snippet; this is asserted at runtime before training starts. 337,398 pairs of the held-out fold were dropped because their two snippets fell on opposite sides of the validation/test group boundary.

Class weighting: Measured majority-class share 0.5000 is within the 0.6 threshold, so weighted cross entropy is NOT used.

Training

optimizeradamw_torch
learning rate2e-05
schedulerlinear with 0.1 warmup ratio (938 steps)
epochs3.0
per-device batch size16
gradient accumulation1
effective batch size16
weight decay0.01
gradient clipping1.0
mixed precisionfp16
gradient checkpointingFalse
seed42
trainable parameters125,236,994
training time1.923 h
GPUNVIDIA GeForce RTX 5060 Ti (15.9 GB)
torch / transformers2.11.0+cu128 / 5.17.0

Checkpoint selection: best validation F1 (load_best_model_at_end=True, metric_for_best_model="f1"). Best validation F1 = 0.8672. The test split was scored once, after selection.

Results

splitaccuracyprecisionrecallF1TPTNFPFN
validation0.85570.80320.94220.86729,4227,6922,308578
test0.87470.84100.92400.88059,2408,2531,747760

Test confusion matrix ([[TN, FP], [FN, TP]]): [[8253, 1747], [760, 9240]]

Usage

This checkpoint uses a custom pairwise head and a graph-guided attention mask, so AutoModelForSequenceClassification will not reproduce these results. Use the repository's own model class and preprocessing:

python
import torch
from transformers import AutoTokenizer
from modeling import load_model                      # from this project
from preprocess import build_snippet_features, CloneCollator
from config import Config

cfg = Config()
tokenizer = AutoTokenizer.from_pretrained("thealper2/graphcodebert-code-clone-detection")
model = load_model("thealper2/graphcodebert-code-clone-detection").eval()

features = build_snippet_features(cfg, [code_a, code_b], tokenizer, num_proc=1)
collator = CloneCollator(features)
batch = collator([(0, 1, 0)])                        # (snippet_a, snippet_b, dummy label)
with torch.no_grad():
    logits = model(**{k: v for k, v in batch.items() if k != "labels"}).logits
label = int(logits.argmax(-1))                       # 0 = not clone, 1 = clone

Limitations

  • Trained on competitive-programming Python solutions grouped by problem; "clone" therefore means solves the same problem, which is closer to semantic (Type-4) similarity than to syntactic copy-paste detection.
  • Data flow is extracted with the Python grammar only. Other languages need the matching tree_sitter_<lang> grammar and DFG_<lang> function.
  • Snippets longer than 512 BPE tokens are truncated; 885 of 44,950 distinct snippets hit that limit.
  • Both directions of a pair are not explicitly symmetrised; the head sees concat(<s>_1, <s>_2) in the given order.