MisileLab commited on
Commit
b761089
·
verified ·
1 Parent(s): cdd292d

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. README.md +237 -0
  2. model.pth +3 -0
README.md ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Vivian - YouTube Bot Comment Detector
2
+
3
+ This model detects bot comments on YouTube videos using a fine-tuned KcELECTRA model with custom classification layers.
4
+
5
+ ## Model Description
6
+
7
+ Vivian is a specialized model for identifying bot-generated comments on YouTube. It leverages the KcELECTRA base model with a custom architecture optimized for handling the class imbalance inherent in bot detection tasks.
8
+
9
+ ### Model Architecture
10
+
11
+ - **Base Model**: [beomi/KcELECTRA-base](https://huggingface.co/beomi/KcELECTRA-base) - A Korean-focused ELECTRA model
12
+ - **Modifications**:
13
+ - Frozen initial transformer layers to prevent overfitting
14
+ - Custom classification layers with dropout for regularization
15
+ - Combined CLS token and mean pooling for improved feature representation
16
+ - Focal Loss implementation to handle class imbalance
17
+
18
+ ### Key Features
19
+
20
+ - Effective on Korean YouTube comments
21
+ - Robust against class imbalance (few bot comments vs. many human comments)
22
+ - Optimized for both precision and recall in bot detection
23
+
24
+ ## Intended Uses
25
+
26
+ This model is designed for:
27
+ - Content moderation on YouTube videos
28
+ - Automated filtering of bot comments
29
+ - Research on bot behavior in social media
30
+
31
+ ## Training Data
32
+
33
+ The model was trained on the [MisileLab/youtube-bot-comments](https://huggingface.co/datasets/MisileLab/youtube-bot-comments) dataset, which contains:
34
+ - YouTube comments collected from popular Korean videos
35
+ - Manual annotations for bot vs. human comments
36
+ - A 70/20/10 train/test/validation split
37
+
38
+ ## Performance
39
+
40
+ The model achieves:
41
+ - High precision in bot detection to minimize false positives
42
+ - Good recall to catch the majority of bot comments
43
+ - Balanced performance across different comment lengths and styles
44
+
45
+ ## Usage
46
+
47
+ ```python
48
+ from transformers import AutoTokenizer, ElectraModel
49
+ import torch
50
+ import torch.nn as nn
51
+
52
+ # Load the tokenizer
53
+ tokenizer = AutoTokenizer.from_pretrained("beomi/KcELECTRA-base")
54
+
55
+ # Define the model architecture (same as in training)
56
+ class SpamUserClassificationLayer(nn.Module):
57
+ def __init__(self, encoder: ElectraModel):
58
+ super().__init__()
59
+
60
+ self.encoder = encoder
61
+
62
+ # Classification network optimized for imbalanced datasets
63
+ # Changed input dimension from 768 to 1536 (CLS + mean pooling)
64
+ self.dense1 = nn.Linear(1536, 512)
65
+ self.layernorm1 = nn.LayerNorm(512)
66
+ self.gelu1 = nn.GELU()
67
+ self.dropout1 = nn.Dropout(0.4)
68
+
69
+ self.dense2 = nn.Linear(512, 256)
70
+ self.layernorm2 = nn.LayerNorm(256)
71
+ self.gelu2 = nn.GELU()
72
+ self.dropout2 = nn.Dropout(0.3)
73
+
74
+ def forward(self, input_ids, attention_mask=None, token_type_ids=None):
75
+ # Get encoder outputs
76
+ outputs = self.encoder(
77
+ input_ids=input_ids,
78
+ attention_mask=attention_mask,
79
+ token_type_ids=token_type_ids,
80
+ output_attentions=True
81
+ )
82
+
83
+ # CLS token representation
84
+ cls_output = outputs.last_hidden_state[:, 0, :] # [batch, 768]
85
+
86
+ # Mean pooling with proper attention masking
87
+ token_embeddings = outputs.last_hidden_state # [batch, seq_len, 768]
88
+ input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
89
+ sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1)
90
+ sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)
91
+ mean_pooled = sum_embeddings / sum_mask # [batch, 768]
92
+
93
+ # Concatenate CLS + mean pooling
94
+ combined_output = torch.cat([cls_output, mean_pooled], dim=1) # [batch, 1536]
95
+
96
+ # Pass through classification network
97
+ x = self.dense1(combined_output)
98
+ x = self.layernorm1(x)
99
+ x = self.gelu1(x)
100
+ x = self.dropout1(x)
101
+
102
+ x = self.dense2(x)
103
+ x = self.layernorm2(x)
104
+ x = self.gelu2(x)
105
+ x = self.dropout2(x)
106
+
107
+ return x
108
+
109
+ class SpamUserClassifier(nn.Module):
110
+ def __init__(self, pretrained_model_name="beomi/kcelectra-base"):
111
+ super().__init__()
112
+
113
+ self.encoder = ElectraModel.from_pretrained(pretrained_model_name)
114
+
115
+ # Freeze first 2 layers for imbalanced dataset scenario
116
+ for i, layer in enumerate(self.encoder.encoder.layer):
117
+ if i < 2:
118
+ for param in layer.parameters():
119
+ param.requires_grad = False
120
+
121
+ self.nameLayer = SpamUserClassificationLayer(self.encoder)
122
+ self.contentLayer = SpamUserClassificationLayer(self.encoder)
123
+
124
+ self.dense = nn.Linear(512, 256)
125
+ self.layernorm = nn.LayerNorm(256)
126
+ self.gelu = nn.GELU()
127
+ self.dropout = nn.Dropout(0.3)
128
+
129
+ self.output_layer = nn.Linear(256, 1)
130
+ self.sigmoid = nn.Sigmoid()
131
+
132
+ def forward(self, name_input_ids, content_input_ids, name_attention_mask=None, name_token_type_ids=None,
133
+ content_attention_mask=None, content_token_type_ids=None, return_logits=False, return_probs=True):
134
+
135
+ namePrediction = self.nameLayer(name_input_ids, name_attention_mask, name_token_type_ids)
136
+ contentPrediction = self.contentLayer(content_input_ids, content_attention_mask, content_token_type_ids)
137
+
138
+ # Pass through classification network
139
+ x = self.dense(torch.cat([namePrediction, contentPrediction], dim=1))
140
+ x = self.layernorm(x)
141
+ x = self.gelu(x)
142
+ x = self.dropout(x)
143
+
144
+ logits = self.output_layer(x)
145
+
146
+ if return_logits:
147
+ return logits
148
+ else:
149
+ # Apply sigmoid and return probabilities or predictions
150
+ probs = self.sigmoid(logits)
151
+ # Return class predictions: 0 (not bot) or 1 (bot)
152
+ return probs if return_probs else (probs > 0.9).long().squeeze(-1)
153
+
154
+ # Load the model
155
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
156
+ model = SpamUserClassifier()
157
+ model.load_state_dict(torch.load("model.pth", map_location=device))
158
+ model.to(device)
159
+ model.eval()
160
+
161
+ # Example inference
162
+ def classify_comment(author_name, comment_text, threshold=0.9):
163
+ # Tokenize author name
164
+ name_encoding = tokenizer(
165
+ author_name,
166
+ truncation=True,
167
+ padding="max_length",
168
+ max_length=128,
169
+ return_tensors="pt"
170
+ )
171
+ name_input_ids = name_encoding["input_ids"].to(device)
172
+ name_attention_mask = name_encoding["attention_mask"].to(device)
173
+
174
+ # Tokenize content
175
+ content_encoding = tokenizer(
176
+ comment_text,
177
+ truncation=True,
178
+ padding="max_length",
179
+ max_length=128,
180
+ return_tensors="pt"
181
+ )
182
+ content_input_ids = content_encoding["input_ids"].to(device)
183
+ content_attention_mask = content_encoding["attention_mask"].to(device)
184
+
185
+ # Get prediction
186
+ with torch.no_grad():
187
+ probs = model(
188
+ name_input_ids=name_input_ids,
189
+ content_input_ids=content_input_ids,
190
+ name_attention_mask=name_attention_mask,
191
+ content_attention_mask=content_attention_mask,
192
+ return_logits=False,
193
+ return_probs=True
194
+ )
195
+
196
+ # Get probability and prediction
197
+ probability = probs.item()
198
+ is_bot = probability > threshold
199
+
200
+ return {
201
+ "probability": probability,
202
+ "is_bot": is_bot
203
+ }
204
+
205
+ # Example usage
206
+ result = classify_comment(
207
+ author_name="SpamBot2023",
208
+ comment_text="Check out my channel for free gift cards!"
209
+ )
210
+ print(f"Bot probability: {result['probability']:.4f}")
211
+ print(f"Is bot comment: {result['is_bot']}")
212
+ ```
213
+
214
+ ## Limitations
215
+
216
+ - Primarily optimized for Korean YouTube comments
217
+ - May have reduced performance on other languages or platforms
218
+ - Cannot detect sophisticated bots that closely mimic human writing patterns
219
+ - Limited to text-based features (doesn't consider user history or behavior patterns)
220
+
221
+ ## Citation
222
+
223
+ If you use this model in your research, please cite:
224
+
225
+ ```
226
+ @misc{vivian-youtube-bot-detector,
227
+ author = {MisileLab},
228
+ title = {Vivian: YouTube Bot Comment Detection System},
229
+ year = {2025},
230
+ publisher = {Hugging Face},
231
+ howpublished = {\url{https://huggingface.co/MisileLab/vivian}}
232
+ }
233
+ ```
234
+
235
+ ## Contact
236
+
237
+ For questions, issues, or feedback, please open an issue on the [GitHub repository](https://github.com/misilelab/h3).
model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8e98c216e4144dc3e557763bf3bdd1cd8d33e9948704e4aaddf047442256227f
3
+ size 442019527