Vector DB로 악플 구분하기 feat. Weaviate
Vector DB 기능을 간단히 테스트 하기 위해 혐오표현 문장을 DB 에 삽입하고, 테스트 하고자 하는 문장이 혐오표현에 가까운지 여부를 판단하는 코드를 작성해본다.
1. 한글 혐오표현 구분
1) Vector DB, Weaviate 설치
Weaviate 를 설치하고 한글 벡터처리를 위해 openai 를 사용하기로 했다.
version: '3.4'
services:
weaviate:
command:
- --host
- 0.0.0.0
- --port
- '8080'
- --scheme
- http
image: semitechnologies/weaviate:1.20.5
ports:
- 8080:8080
restart: on-failure:0
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
CLUSTER_HOSTNAME: 'node1'
DEFAULT_VECTORIZER_MODULE: 'text2vec-openai'
ENABLE_MODULES: 'text2vec-openai'
OPENAI_APIKEY: 'sk-????????????????????????????????????'
2) Weaviate 데이터 스키마 구성
한글 테스트 데이터셋은 스마일게이트에서 공개한 혐오표현 데이터를 사용하기로 한다
import pandas as pd
import json
df = pd.read_csv("./unsmile_train_v1.0.tsv", sep='\t')
from weaviate import Client
client = Client("http://localhost:8080")
client.schema.create_class({
"class": "HateSpeech",
"description": "HateSpeech Collection",
"properties": [
{
"dataType": ["text"],
"name": "speech",
},
{
"dataType": ["int"],
"name": "clean",
},
],
"vectorizer": "text2vec-openai",
})
3) 데이터 삽입
# 데이터 정리
schemas = []
for idx in range(len(df)):
item = df.iloc[idx]
speech = str(item['문장']).strip().lower()
clean = str(item['clean'])
schemas.append({
"speech" : speech,
"clean": 1 if clean == '1' else 0,
})
# OpenAI 에서 Vector 값 받아 Insert
with client.batch(
batch_size=200,
num_workers=1,
dynamic=True
) as batch:
for k, v in enumerate(schemas):
res = client.batch.add_data_object(
v, "HateSpeech",
)
print(k)
print(v)
time.sleep(5)
# 문장 체크
def check(text):
return client.query.get("HateSpeech", ["clean"]).with_near_text({"concepts": [
text
],'certainty': 0.7}).with_limit(1).with_additional(["distance"]).do()
4) 한글 혐오표현 구분 테스트
clean 값이 0인 경우가 혐오표현으로 판단된 문장인데, 노골적인 문장을 제외하고 몇몇 문장은 오탐을 하는 경우도 있었다.
뒤늦게 생각해보니 weaviate 는 문장을 넣을때 문장을 tokenize 해서 벡터화 하고 있을까 하는 의문이 들었다.
2. 영문 혐오표현 구분
1) Weaviate, Vector Module 변경
weaviate 는 다양한 Vector Module 을 제공하고 있다.
한글 벡터는 OpenAI 또는 Huggingface 를 써야 하는데 call limit 이 있어서 로컬에서 편하게 사용할 수 있는 text2vec-contextionary 를 쓰기로 했고 다음과 같이 세팅한다.
version: '3.4'
services:
weaviate:
command:
- --host
- 0.0.0.0
- --port
- '8080'
- --scheme
- http
image: semitechnologies/weaviate:1.20.5
ports:
- 8080:8080
restart: on-failure:0
environment:
CONTEXTIONARY_URL: contextionary:9999
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
PERSISTENCE_DATA_PATH: './data'
DEFAULT_VECTORIZER_MODULE: 'text2vec-contextionary'
ENABLE_MODULES: 'text2vec-contextionary'
CLUSTER_HOSTNAME: 'node1'
contextionary:
environment:
OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75
EXTENSIONS_STORAGE_MODE: weaviate
EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080
NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5
ENABLE_COMPOUND_SPLITTING: 'false'
image: semitechnologies/contextionary:en0.16.0-v1.0.2
ports:
- 9999:9999
로컬에서 한글을 Vector Module 을 사용하고 싶은 경우는 text2vec-transformer 를 사용하면 되는데, Huggingface 의 KoBert 모델을 가져다 쓰려다가 잘 동작하지 않는 듯 해서 시간 날때 다시 보기로 한다.
2) Weaviate 데이터 스키마 구성
영문 테스트 데이터셋은 케글에 있는 데이터를 사용했다.
client.schema.create_class({
"class": "HateSpeech",
"description": "HateSpeech Collection",
"properties": [
{
"dataType": ["text[]"],
"name": "speech",
},
{
"dataType": ["int"],
"name": "clean",
},
],
"vectorizer": "text2vec-contextionary",
})
weaviate 에 문장으로 insert 를 하면, 왠지 문장 전체를 벡터화 하는 듯 해서, 테스트 문장을 따로 Tokenize 해서 배열로 넣어보기로 했다.
3) 데이터 삽입
from tensorflow.keras.preprocessing.text import text_to_word_sequence
schemas = []
for idx in range(len(df)):
item = df.iloc[idx]
speech = str(item['comment_text']).strip().lower()
hate = str(item['toxic'])
schemas.append({
"speech" : text_to_word_sequence(speech),
"clean" : 0 if hate == '1' else 1,
})
4) 영문 혐오표현 구분 테스트
clean 값이 0인 경우가 혐오표현으로 판단된 문장이고, 문장을 Tokenize 해서 넣었을때가 weaviate가 자동으로 tokenize 해서 넣주길 바랬을때 보다 정확하게 탐지하는 것을 볼 수 있었다. 이 경우는 Text 를 DB에 넣었을때 상황인데, 이미지를 벡터화 하는 경우는 어떨지 모르겠다.