<jupyter_start><jupyter_text>Sentence Transformers 학습과 활용 본 노트북에서는 `klue/roberta-base` 모델을 **KLUE** 내 **STS** 데이터셋을 활용하여 모델을 훈련하는 예제를 다루게 됩니다.학습을 통해 얻어질 `sentence-klue-roberta-base` 모델은 입력된 문장의 임베딩을 계산해 유사도를 예측하는데 사용할 수 있게 됩니다.학습 과정 이후에는 간단한 예제 코드를 통해 모델이 어떻게 활용되는지도 함께 알아보도록 할 것입니다.모든 소스 코드는 [`sentence-transformers`](https://github.com/UKPLab/sentence-transformers) 원 라이브러리를 참고하였습니다.먼저, 노트북을 실행하는데 필요한 라이브러리를 설치합니다. 모델 훈련을 위해서는 `sentence-transformers`가, 학습 데이터셋 로드를 위해서는 `datasets` 라이브러리의 설치가 필요합니다.<jupyter_code>!pip install sentence-transformers datasets<jupyter_output><empty_output><jupyter_text>Sentence Transformers 학습 노트북을 실행하는데 필요한 라이브러리들을 모두 임포트합니다.<jupyter_code>import math
import logging
from datetime import datetime

import torch
from torch.utils.data import DataLoader
from datasets import load_dataset
from sentence_transformers import SentenceTransformer,  LoggingHandler, losses, models, util
from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator
from sentence_transformers.readers import InputExample<jupyter_output><empty_output><jupyter_text>학습 경과를 지켜보는데 사용될 *logger* 를 초기화합니다.<jupyter_code>logging.basicConfig(
    format="%(asctime)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    level=logging.INFO,
    handlers=[LoggingHandler()],
)<jupyter_output><empty_output><jupyter_text>학습에 필요한 정보를 변수로 기록합니다.본 노트북에서는 `klue-roberta-base` 모델을 활용하지만, https://huggingface.co/klue 페이지에서 더 다양한 사전학습 언어 모델을 확인하실 수 있습니다.<jupyter_code>model_name = "klue/roberta-base"<jupyter_output><empty_output><jupyter_text>모델 정보 외에도 학습에 필요한 하이퍼 파라미터를 정의합니다.<jupyter_code>train_batch_size = 32
num_epochs = 4
model_save_path = "output/training_klue_sts_" + model_name.replace("/", "-") + "-" + datetime.now().strftime("%Y-%m-%d_%H-%M-%S")<jupyter_output><empty_output><jupyter_text>앞서 정의한 사전학습 언어 모델을 로드합니다.`sentence-transformers`는 HuggingFace의 `transformers`와 호환이 잘 이루어지고 있기 때문에, [모델 허브](https://huggingface.co/models)에 올라와있는 대부분의 언어 모델을 임베딩을 추출할 *Embedder* 로 사용할 수 있습니다.<jupyter_code>embedding_model = models.Transformer(model_name)<jupyter_output><empty_output><jupyter_text>*Embedder* 에서 추출된 토큰 단위 임베딩들을 가지고 문장 임베딩을 어떻게 계산할 것인지를 결정하는 *Pooler* 를 정의합니다.여러 Pooling 기법이 있겠지만, 예제 노트북에서는 **Mean Pooling**을 사용하기로 합니다.**Mean Pooling**이란 모델이 반환한 모든 토큰 임베딩을 더해준 후, 더해진 토큰 개수만큼 나누어 문장을 대표하는 임베딩으로 사용하는 기법을 의미합니다.<jupyter_code>pooler = models.Pooling(
    embedding_model.get_word_embedding_dimension(),
    pooling_mode_mean_tokens=True,
    pooling_mode_cls_token=False,
    pooling_mode_max_tokens=False,
)<jupyter_output><empty_output><jupyter_text>*Embedder* 와 *Pooler* 를 정의했으므로, 이 두 모듈로 구성된 하나의 모델을 정의합니다.`modules`에 입력으로 들어가는 모듈이 순차적으로 임베딩 과정에 사용이 된다고 생각하시면 됩니다.<jupyter_code>model = SentenceTransformer(modules=[embedding_model, pooler])<jupyter_output>2021-06-18 16:26:30 - Use pytorch device: cuda<jupyter_text>이제 학습에 사용될 KLUE STS 데이터셋을 다운로드 및 로드합니다.<jupyter_code>datasets = load_dataset("klue", "sts")<jupyter_output><empty_output><jupyter_text>다운로드 혹은 로드 후 얻어진 `datasets` 객체를 살펴보면, 훈련 데이터와 검증 데이터가 포함되어 있는 것을 확인할 수 있습니다.<jupyter_code>datasets<jupyter_output><empty_output><jupyter_text>각 예시 데이터는 아래와 같이 두 개의 문장과 두 문장의 유사도를 라벨로 지니고 있습니다.<jupyter_code>datasets["train"][0]<jupyter_output><empty_output><jupyter_text>이제 테스트에 활용할 데이터를 얻어야 할 차례입니다.위에서 살펴본 바와 같이 KLUE 내 STS 데이터셋은 테스트 데이터셋을 포함하고 있지 않습니다.따라서 실습의 원활한 진행을 위해 다른 벤치마크 STS 데이터셋인 KorSTS 데이터셋을 다운로드 및 로드하여 사용하도록 하겠습니다.(\* 두 데이터셋은 제작 과정이 엄밀히 다르므로, KLUE STS 데이터에 대해 학습된 모델이 KorSTS 테스트셋에 대해 기록하는 점수은 사실상 큰 의미가 없을 수 있습니다. 전체적인 훈련 프로세스의 이해를 돕기 위해 사용한다고 생각해주시는게 좋습니다.)<jupyter_code>testsets = load_dataset("kor_nlu", "sts")<jupyter_output><empty_output><jupyter_text>KorSTS 데이터셋은 훈련, 검증 그리고 테스트셋을 지니고 있습니다.<jupyter_code>testsets<jupyter_output><empty_output><jupyter_text>KorSTS의 예시 데이터도 마찬가지로 두 문장과 두 문장 간 유사도를 지니고 있습니다.<jupyter_code>testsets["test"][0]<jupyter_output><empty_output><jupyter_text>이제 앞서 얻어진 데이터셋을 `sentence-transformers` 훈련 양식에 맞게 변환해주는 작업을 거쳐야 합니다.두 데이터 모두 0점에서 5점 사이의 값으로 유사도가 기록되었기 때문에, 0.0 ~ 1.0 스케일로 정규화를 시켜주는 작업을 거치게 됩니다.(\* KorSTS 내 테스트셋의 경우 `None`으로 기록된 문장이 몇 개 존재하여, `None`을 걸러주는 조건이 추가되었습니다.)<jupyter_code>train_samples = []
dev_samples = []
test_samples = []

# KLUE STS 내 훈련, 검증 데이터 예제 변환
for phase in ["train", "validation"]:
    examples = datasets[phase]

    for example in examples:
        score = float(example["labels"]["label"]) / 5.0  # 0.0 ~ 1.0 스케일로 유사도 정규화

        inp_example = InputExample(
            texts=[example["sentence1"], example["sentence2"]], 
            label=score,
        )

        if phase == "validation":
            dev_samples.append(inp_example)
        else:
            train_samples.append(inp_example)

# KorSTS 내 테스트 데이터 예제 변환
for example in testsets["test"]:
    score = float(example["score"]) / 5.0

    if example["sentence1"] and example["sentence2"]:
        inp_example = InputExample(
            texts=[example["sentence1"], example["sentence2"]],
            label=score,
        )

    test_samples.append(inp_example)<jupyter_output><empty_output><jupyter_text>앞선 로직을 통해 각 데이터 예제는 다음과 같이 `InputExample` 객체로 변환되게 됩니다.<jupyter_code>train_samples[0].texts, train_samples[0].label
test_samples[0].texts, test_samples[0].label<jupyter_output><empty_output><jupyter_text>이제 학습에 사용될 `DataLoader`와 **Loss**를 설정해주도록 합니다.`CosineSimilarityLoss`는 입력된 두 문장의 임베딩 간 코사인 유사도와 골드 라벨 간 차이를 통해 계산되게 됩니다.<jupyter_code>train_dataloader = DataLoader(
    train_samples,
    shuffle=True,
    batch_size=train_batch_size,
)
train_loss = losses.CosineSimilarityLoss(model=model)<jupyter_output><empty_output><jupyter_text>모델 검증에 활용할 **Evaluator** 를 정의해줍니다.앞서 얻어진 검증 데이터를 활용하여, 모델의 문장 임베딩 간 코사인 유사도가 얼마나 골드 라벨에 가까운지 계산하는 역할을 수행합니다.<jupyter_code>evaluator = EmbeddingSimilarityEvaluator.from_input_examples(
    dev_samples,
    name="sts-dev",
)<jupyter_output><empty_output><jupyter_text>모델 학습에 사용될 **Warm up Steps**를 설정합니다.다양한 방법으로 스텝 수를 결정할 수 있겠지만, 예제 노트북에서는 원 예제 코드를 따라 훈련 배치 수의 10% 만큼으로 값을 설정합니다.<jupyter_code>warmup_steps = math.ceil(len(train_dataloader) * num_epochs  * 0.1)  # 10% of train data for warm-up
logging.info(f"Warmup-steps: {warmup_steps}")<jupyter_output>2021-06-18 16:28:19 - Warmup-steps: 146<jupyter_text>이제 앞서 얻어진 객체, 값들을 가지고 모델의 훈련을 진행합니다.`sentence-transformers`에서는 다음과 같이 `fit` 함수를 통해 간단히 모델의 훈련과 검증이 가능합니다.훈련 과정을 통해 매 에폭 마다 얻어지는 체크포인트에 대해 *Evaluator* 가 학습된 모델의 코사인 유사도와 골드 라벨 간 피어슨, 스피어만 상관 계수를 계산해 기록을 남기게 됩니다.<jupyter_code>model.fit(
    train_objectives=[(train_dataloader, train_loss)],
    evaluator=evaluator,
    epochs=num_epochs,
    evaluation_steps=1000,
    warmup_steps=warmup_steps,
    output_path=model_save_path,
)<jupyter_output><empty_output><jupyter_text>학습이 완료되었다면 이제 학습된 모델을 테스트 할 시간입니다.앞서 KorSTS 데이터를 활용해 구축한 테스트 데이터셋을 앞서와 마찬가지로 *Evaluator* 로 초기화해주도록 합니다.<jupyter_code>model = SentenceTransformer(model_save_path)
test_evaluator = EmbeddingSimilarityEvaluator.from_input_examples(test_samples, name='sts-test')<jupyter_output>2021-06-18 16:43:02 - Load pretrained SentenceTransformer: output/training_klue_sts_klue-roberta-base-2021-06-18_16-26-03
2021-06-18 16:43:02 - Load SentenceTransformer from folder: output/training_klue_sts_klue-roberta-base-2021-06-18_16-26-03
2021-06-18 16:43:03 - Use pytorch device: cuda<jupyter_text>이제 테스트 *Evaluator* 를 활용하여 테스트셋에 대해 각 상관 계수를 계산하도록 합니다.<jupyter_code>test_evaluator(model, output_path=model_save_path)<jupyter_output>2021-06-18 16:43:07 - EmbeddingSimilarityEvaluator: Evaluating the model on sts-test dataset:
2021-06-18 16:43:11 - Cosine-Similarity :	Pearson: 0.7758	Spearman: 0.7639
2021-06-18 16:43:11 - Manhattan-Distance:	Pearson: 0.7695	Spearman: 0.7660
2021-06-18 16:43:11 - Euclidean-Distance:	Pearson: 0.7694	Spearman: 0.7660
2021-06-18 16:43:11 - Dot-Product-Similarity:	Pearson: 0.7429	Spearman: 0.7300<jupyter_text>역시 검증 데이터에 비해 좋지 않은 점수를 기록하였습니다.KLUE 내 검증 데이터셋 중 일부를 샘플링하여 테스트셋으로 활용하는 방안도 있겠지만,본 노트북은 전체 훈련 프로세스를 파악하는데 초점을 맞추었으므로 실험을 마치도록 합니다.  Sentence Transformers 활용  시맨틱 서치 입력된 문장 간 유사도를 쉽고 빠르게 구할 수 있도록 설계된 `sentence-transformers`를 이용한다면 임베딩을 활용해 다양한 어플리케이션을 고안할 수 있습니다.먼저 여러 문장 후보군이 주어졌을 때, 입력된 문장과 가장 유사한 문장을 계산하는 예제를 살펴보도록 합시다.이를 위해 검색의 대상이 되는 문장 후보군을 다음과 같이 정의할 필요가 있습니다. 이후, 정의된 문장 후보군을 미리 임베딩합니다.<jupyter_code>docs = [
    "1992년 7월 8일 손흥민은 강원도 춘천시 후평동에서 아버지 손웅정과 어머니 길은자의 차남으로 태어나 그곳에서 자랐다.",
    "형은 손흥윤이다.",
    "춘천 부안초등학교를 졸업했고, 춘천 후평중학교에 입학한 후 2학년때 원주 육민관중학교 축구부에 들어가기 위해 전학하여 졸업하였으며, 2008년 당시 FC 서울의 U-18팀이었던 동북고등학교 축구부에서 선수 활동 중 대한축구협회 우수선수 해외유학 프로젝트에 선발되어 2008년 8월 독일 분데스리가의 함부르크 유소년팀에 입단하였다.",
    "함부르크 유스팀 주전 공격수로 2008년 6월 네덜란드에서 열린 4개국 경기에서 4게임에 출전, 3골을 터뜨렸다.",
    "1년간의 유학 후 2009년 8월 한국으로 돌아온 후 10월에 개막한 FIFA U-17 월드컵에 출전하여 3골을 터트리며 한국을 8강으로 이끌었다.",
    "그해 11월 함부르크의 정식 유소년팀 선수 계약을 체결하였으며 독일 U-19 리그 4경기 2골을 넣고 2군 리그에 출전을 시작했다.",
    "독일 U-19 리그에서 손흥민은 11경기 6골, 2부 리그에서는 6경기 1골을 넣으며 재능을 인정받아 2010년 6월 17세의 나이로 함부르크의 1군 팀 훈련에 참가, 프리시즌 활약으로 함부르크와 정식 계약을 한 후 10월 18세에 함부르크 1군 소속으로 독일 분데스리가에 데뷔하였다.",
]
document_embeddings = model.encode(docs)<jupyter_output><empty_output><jupyter_text>이제 입력 문장을 임베딩 할 차례입니다.<jupyter_code>query = "손흥민은 어린 나이에 유럽에 진출하였다."
query_embedding = model.encode(query)<jupyter_output><empty_output><jupyter_text>아래는 입력된 문장의 임베딩과 미리 임베딩 된 후보군 문장 임베딩 간 유사도를 계산해 유사도가 높은 순서대로 `top_k` 개 문장을 뽑아주는 예제 코드입니다.`top_k`는 전체 문장 후보군의 개수를 넘지 않아야 하므로, `min()` 함수를 통해 예외 처리를 해줍니다.<jupyter_code>top_k = min(5, len(docs))

# 입력 문장 - 문장 후보군 간 코사인 유사도 계산 후,
cos_scores = util.pytorch_cos_sim(query_embedding, document_embeddings)[0]

# 코사인 유사도 순으로 `top_k` 개 문장 추출
top_results = torch.topk(cos_scores, k=top_k)

print(f"입력 문장: {query}")
print(f"\n<입력 문장과 유사한 {top_k} 개의 문장>\n")

for i, (score, idx) in enumerate(zip(top_results[0], top_results[1])):
    print(f"{i+1}: {docs[idx]} {'(유사도: {:.4f})'.format(score)}\n")<jupyter_output>입력 문장: 손흥민은 어린 나이에 유럽에 진출하였다.

<입력 문장과 유사한 5 개의 문장>

1: 독일 U-19 리그에서 손흥민은 11경기 6골, 2부 리그에서는 6경기 1골을 넣으며 재능을 인정받아 2010년 6월 17세의 나이로 함부르크의 1군 팀 훈련에 참가, 프리시즌 활약으로 함부르크와 정식 계약을 한 후 10월 18세에 함부르크 1군 소속으로 독일 분데스리가에 데뷔하였다. (유사도: 0.5897)

2: 그해 11월 함부르크의 정식 유소년팀 선수 계약을 체결하였으며 독일 U-19 리그 4경기 2골을 넣고 2군 리그에 출전을 시작했다. (유사도: 0.4857)

3: 1992년 7월 8일 손흥민은 강원도 춘천시 후평동에서 아버지 손웅정과 어머니 길은자의 차남으로 태어나 그곳에서 자랐다. (유사도: 0.4047)

4: 함부르크 유스팀 주전 공격수로 2008년 6월 네덜란드에서 열린 4개국 경기에서 4게임에 출전, 3골을 터뜨렸다. (유사도: 0.3953)

5: 춘천 부안초등학교를 졸업했고, 춘천 후평중학교에 입학한 후 2학년때 원주 육민관중학교 축구부에 들어가기 위해 전학하여 졸업하였으며, 2008년 당시 FC 서울의 U-18팀이었던 동북고등학교 축구부에서 선수 활동 중 대한축구협회 우수선수 해외유학 프로젝트에 선발되어 2008년 8월 독일 분데스리가의 함부르크 유소년팀에 입단하였다. (유사도: 0.3183)<jupyter_text>반환된 문장 중 `유럽`, `어린 나이` 등의 키워드가 없음애도 높은 유사도로 두 문장이 반환된 것을 확인할 수 있습니다.  클러스터링 `sentence-transformers`를 통해 얻어진 임베딩을 활용해 클러스터링을 수행할 수도 있습니다.다양한 클러스터링 기법의 적용이 가능하겠지만, 본 노트북에서는 **k-Means** 클러스터링을 수행한 결과를 살펴보도록 합니다.예제 수행을 위해 `scikit-learn`의 설치가 추가로 필요합니다.<jupyter_code># !pip install scikit-learn
from sklearn.cluster import KMeans<jupyter_output><empty_output><jupyter_text>마찬가지로 앞서 구축한 문장 후보군들에 대해 임베딩을 수행합니다.이후, `num_clusters` 변수를 통해 클러스터의 개수를 설정한 후 임베딩을 활용한 **k-Means** 클러스터링을 수행하도록 합니다.<jupyter_code>document_embeddings = model.encode(docs)

num_clusters = 3

k_means = KMeans(n_clusters=num_clusters)
k_means.fit(document_embeddings)<jupyter_output><empty_output><jupyter_text>이제 클러스터링을 통해 각 문장이 어떤 클러스터에 포함되었는지 확인해봅시다.<jupyter_code>cluster_assignment = k_means.labels_
cluster_assignment<jupyter_output><empty_output><jupyter_text>클러스터링 결과를 토대로 각 문장을 클러스터로 분리한 후, 결과를 출력합니다.<jupyter_code># 클러스터 개수 만큼 문장을 담을 리스트 초기화
clustered_sentences = [[] for _ in range(num_clusters)]

# 클러스터링 결과를 돌며 각 클러스터에 맞게 문장 삽입
for sentence_id, cluster_id in enumerate(cluster_assignment):
    clustered_sentences[cluster_id].append(docs[sentence_id])

for i, cluster in enumerate(clustered_sentences):
    result = "\n".join(cluster)
    print(f"< 클러스터 {i+1} >\n{result}\n")<jupyter_output>< 클러스터 1 >
1992년 7월 8일 손흥민은 강원도 춘천시 후평동에서 아버지 손웅정과 어머니 길은자의 차남으로 태어나 그곳에서 자랐다.
춘천 부안초등학교를 졸업했고, 춘천 후평중학교에 입학한 후 2학년때 원주 육민관중학교 축구부에 들어가기 위해 전학하여 졸업하였으며, 2008년 당시 FC 서울의 U-18팀이었던 동북고등학교 축구부에서 선수 활동 중 대한축구협회 우수선수 해외유학 프로젝트에 선발되어 2008년 8월 독일 분데스리가의 함부르크 유소년팀에 입단하였다.
1년간의 유학 후 2009년 8월 한국으로 돌아온 후 10월에 개막한 FIFA U-17 월드컵에 출전하여 3골을 터트리며 한국을 8강으로 이끌었다.

< 클러스터 2 >
함부르크 유스팀 주전 공격수로 2008년 6월 네덜란드에서 열린 4개국 경기에서 4게임에 출전, 3골을 터뜨렸다.
그해 11월 함부르크의 정식 유소년팀 선수 계약을 체결하였으며 독일 U-19 리그 4경기 2골을 넣고 2군 리그에 출전을 시작했다.
독일 U-19 리그에서 손흥민은 11경기 6골, 2부 리그에서는 6경기 1골을 넣으며 재능을 인정받아 2010년 6월 17세의 나이로 함부르크의 1군 팀 훈련에 참가, 프리시즌 활약으로 함부르크와 정식 계약을 한 후 10월 18세에 함부르크 1군 소속으로 독일 분데스리가에 데뷔하였다.

< 클러스터 3 >
형은 손흥윤이다.<jupyter_text>국내 관련 문장과 해외 관련 문장, 가족 관계와 같은 느낌으로 클러스터가 형성된 것을 확인할 수 있습니다.지금까지 `sentence-transformers`를 학습하는 과정을 KLUE STS 데이터셋을 통해 알아보았습니다.`sentence-transformers`는 다양한 문장 임베딩 기법과 이를 활용한 응용 사례에 대해서 크게 고민하는 **UKPLab**에서 관리되는 라이브러리이니 만큼 앞으로 더 발전하고 관리될 가능성이 높은 도구입니다.본 노트북을 통해 습득한 지식이 여러분의 업무와 학습에 도움이 되었으면 좋겠습니다.```허 훈 (<EMAIL>)``` APPENDIX: 앞서 학습된 모델을 HuggingFace 모델 허브에 업로드하였으니, 아래 예제와 같이 사용이 가능합니다.<jupyter_code>import torch
from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer("Huffon/sentence-klue-roberta-base")

docs = [
    "1992년 7월 8일 손흥민은 강원도 춘천시 후평동에서 아버지 손웅정과 어머니 길은자의 차남으로 태어나 그곳에서 자랐다.",
    "형은 손흥윤이다.",
    "춘천 부안초등학교를 졸업했고, 춘천 후평중학교에 입학한 후 2학년때 원주 육민관중학교 축구부에 들어가기 위해 전학하여 졸업하였으며, 2008년 당시 FC 서울의 U-18팀이었던 동북고등학교 축구부에서 선수 활동 중 대한축구협회 우수선수 해외유학 프로젝트에 선발되어 2008년 8월 독일 분데스리가의 함부르크 유소년팀에 입단하였다.",
    "함부르크 유스팀 주전 공격수로 2008년 6월 네덜란드에서 열린 4개국 경기에서 4게임에 출전, 3골을 터뜨렸다.",
    "1년간의 유학 후 2009년 8월 한국으로 돌아온 후 10월에 개막한 FIFA U-17 월드컵에 출전하여 3골을 터트리며 한국을 8강으로 이끌었다.",
    "그해 11월 함부르크의 정식 유소년팀 선수 계약을 체결하였으며 독일 U-19 리그 4경기 2골을 넣고 2군 리그에 출전을 시작했다.",
    "독일 U-19 리그에서 손흥민은 11경기 6골, 2부 리그에서는 6경기 1골을 넣으며 재능을 인정받아 2010년 6월 17세의 나이로 함부르크의 1군 팀 훈련에 참가, 프리시즌 활약으로 함부르크와 정식 계약을 한 후 10월 18세에 함부르크 1군 소속으로 독일 분데스리가에 데뷔하였다.",
]
document_embeddings = model.encode(docs)

query = "손흥민은 어린 나이에 유럽에 진출하였다."
query_embedding = model.encode(query)

top_k = min(5, len(docs))

# 입력 문장 - 문장 후보군 간 코사인 유사도 계산 후,
cos_scores = util.pytorch_cos_sim(query_embedding, document_embeddings)[0]

# 코사인 유사도 순으로 `top_k` 개 문장 추출
top_results = torch.topk(cos_scores, k=top_k)

print(f"입력 문장: {query}")
print(f"\n<입력 문장과 유사한 {top_k} 개의 문장>\n")

for i, (score, idx) in enumerate(zip(top_results[0], top_results[1])):
    print(f"{i+1}: {docs[idx]} {'(유사도: {:.4f})'.format(score)}\n")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09645780412080159, 'fraction_numerical': 0.03880891899520181, 'mean_word_length': 4.6942547207713945, 'pattern_counts': {'":': 1, '<': 123, '<?xml version=': 0, '>': 123, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '30', 'dir': 'jupyter-structured-clean-dedup', 'id': '143041', 'license': 'Apache-2.0', 'n_tokens_mistral': 10085, 'n_tokens_neox': 12421, 'n_words': 1756, 'path': 'sentence_transformers.ipynb', 'repo_name': 'Huffon/klue-transformers-tutorial', 'size': '14184'}