TL;DR
LINE은 OpenChat 방 이름과 설명의 유해성을 판정하는 실시간 모니터링 모델을 Granite Guardian 3.1 2B 기반으로 개발했습니다. 수동 검토 데이터의 라벨 불일치를 severity와 reason 빈도 규칙으로 정제하고, penalty code와 reason을 고정 후보 토큰의 생성 확률로 순차 예측하도록 학습했으며 LoRA와 KV caching으로 학습·추론 비용을 줄였습니다. 세 국가의 평가에서 정상·유해 클래스 F1이 이전 모델보다 모두 개선됐고, threshold로 정밀도를 높여 자동 검토 범위를 확장했습니다. 다만 threshold 적용으로 재현율이 낮아졌으며, 향후 데이터 품질별 학습 가중치와 대형 모델을 활용한 cascading을 검토할 계획입니다.
섹션별 상세
# Pseudocode for data cleansing
FOR EACH group IN GROUP_BY(records, key = (name, description)):
codes = SORT_DESC(group.penalty_codes, key = severity_rank)
top_code = FIRST(codes)
# Use the most severe penalty if it appears at least twice
IF COUNT(codes, top_code) >= 2:
final_code = top_code
# Otherwise, use the second-most severe penalty
ELSE:
final_code = SECOND_HIGHEST(codes, key = severity_rank)
reason_count = COUNT_BY_VALUE(group.penalty_reasons)
candidates = ARGMAX_ALL(reason_count)
# Use the most frequent reason
IF SIZE(candidates) = 1:
final_reason = FIRST(candidates)
# Break ties by choosing the globally rarer reason
ELSE:
final_reason = ARGMIN(candidates, key = global_reason_frequency)
EMIT(group.name, group.description, final_code, final_reason)동일한 OpenChat 이름과 설명에 서로 다른 penalty code와 reason이 부여된 기록을 하나의 학습 라벨로 정제합니다.
class OndfPredictor:
...
self.penalty_code_ids = [self.tokenizer.convert_tokens_to_ids(t) for t in PENALTY_CODE_TOKENS]
self.penalty_reason_ids = [self.tokenizer.convert_tokens_to_ids(t) for t in PENALTY_REASON_TOKENS]
# exclude
self.inner_template = self.tokenizer('
Reason:', add_special_tokens=False).input_ids
...
# (1) predict penalty_code
outputs = self.model(**inputs, use_cache=True)
logits = outputs.logits
past_key_values = outputs.past_key_values
# cache for (2)
last_token_logits = logits[:, -1, :]
penalty_code_logits = last_token_logits[:, self.penalty_code_ids]
penalty_code_probs = torch.softmax(penalty_code_logits, dim=1)
pred_code_scores, pred_code_ids = torch.max(penalty_code_probs, dim=1)
# map subset indices back to original token IDs & add '
Reason:' tokens after penalty_code
pred_code_ids = torch.tensor([[self.penalty_code_ids[i]] + self.inner_template for i in pred_code_ids.cpu().tolist()]).to(self.device)
# (2) predict penalty_reason
outputs_step2 = self.model(
input_ids=pred_code_ids,
past_key_values=past_key_values,
use_cache=True
)
last_token_logits_2 = outputs_step2.logits[:, -1, :]
penalty_reason_logits = last_token_logits_2[:, self.penalty_reason_ids]
penalty_reason_probs = torch.softmax(penalty_reason_logits, dim=1)
pred_reason_scores, pred_reason_ids = torch.max(penalty_reason_probs, dim=1)
# map subset indices back to original token IDs
pred_reason_ids = [self.penalty_reason_ids[i] for i in pred_reason_ids.cpu().tolist()]penalty code의 후보 토큰 확률을 먼저 계산한 뒤 KV cache를 재사용해 해당 reason의 후보 토큰 확률을 계산합니다.



용어 해설
- F1 점수(F1 score)
- — 정밀도와 재현율의 조화평균으로, 정상 또는 유해 클래스를 얼마나 균형 있게 판별하는지 나타내는 평가 지표입니다. 한쪽 지표만 높아지는 문제를 줄여 자동 처리에 필요한 전체 판별 품질을 함께 평가합니다.
- 정밀도와 재현율의 상충 관계(Precision-Recall Trade-off)
- — 모델이 정상으로 분류하는 기준을 바꾸면 정밀도와 재현율이 서로 반대 방향으로 움직이는 현상입니다. 자동 처리에서는 잘못된 정상 판정을 줄이기 위해 정밀도를 우선하고, 그 결과 일부 재현율을 감수할 수 있습니다.
- KV 캐싱(KV Caching)
- — Decoder Transformer가 앞선 토큰에서 계산한 key와 value를 저장해 다음 토큰 처리에 재사용하는 추론 최적화 기법입니다. 입력 prefix를 반복 계산하지 않아 penalty code와 reason을 연속 예측할 때 지연과 연산량을 줄입니다.
- 후보 토큰 확률 분류(Candidate Token Probability)
- — 분류 head가 별도 점수를 내도록 하는 대신, 미리 정한 답변 토큰의 생성 확률을 비교해 클래스를 결정하는 방식입니다. 생성 모델의 확률 출력을 활용하면서 예상하지 못한 긴 문장이나 출력 형식 변동을 피할 수 있습니다.
- 데이터 정제(Data Cleansing)
- — 같은 OpenChat 이름과 설명에 서로 다른 penalty 결과가 붙은 사례를 규칙으로 통합해 학습 라벨의 불일치를 줄이는 과정입니다. 최고 severity 빈도와 penalty reason의 전역 빈도를 이용해 noise를 완화하고 일관된 학습 데이터를 만듭니다.
기술
- OpenChat
- Granite Guardian 3.1 2B
- LoRA
- Apache license
- cross entropy loss
- KV caching
- PyTorch
- tokenizer
활용 사례
- OpenChat 방 이름과 설명의 유해성 자동 판정
- penalty level과 penalty reason 예측
- 정상 콘텐츠의 자동 처리
- 자동 처리 기준을 충족하지 못한 사례의 수동 검토 라우팅
AI 요약 · 북마크 · 개인 피드 설정 — 무료
출처 · 인용 안내
인용 시 "요약 출처: AI Trends (aitrends.kr)"를 표기하고, 사실 확인은 원문 보기 기준으로 진행해 주세요. 자세한 기준은 운영 정책을 참고해 주세요.