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

Update README.md

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