본문 바로가기

MACHINE LEARNING

Scikit-Learn을 활용한 가짜 뉴스 탐지

이 문서는 영문으로된 내용을 카카오 번역기를 활용하여 번역한 내용입니다. 
개인적인 공부 및 추후 다시 볼 수 있도록 하기 위해 개인 블로그에 번역 내용을 옮겨 놓았습니다.
원문과 내용이 다를시 책임지지 않으며, 저작권 문제가 발생시 언제든 삭제 될 수 있습니다. 

원문보기 : https://www.datacamp.com/community/tutorials/scikit-learn-fake-news


소위 ‘가짜 뉴스’를 찾아내는 것은 쉬운 일이 아니다. 첫째, 가짜 뉴스가 정치적 진술이 되었다는 점에서 가짜 뉴스가 무엇인지 정의하고 있습니다. 정의를 찾거나 동의할 수 있다면, 실제 뉴스와 가짜 뉴스를 수집하고 적절하게 분류해야 한다(명확한 구별을 가장 잘 보여주기 위해 유사한 주제에 대해 희망적으로). 일단 수집되면 실제 뉴스에서 가짜를 판단하기 위해 유용한 기능을 찾아야 합니다.

이 문제 공간을보다 자세히 살펴 보려면 Miguel Martinez-Alvarez의 "기계 학습 및 AI가 가짜 뉴스 문제를 해결하는 데 어떻게 도움이 되는지"를 살펴 보도록 권장합니다.

미구엘의 통찰력 있는 글을 읽은 것과 동시에 베이지안 모델로 가짜 뉴스 탐지기를 성공적으로 구축하는 것에 관한 공개 데이터 과학 게시물을 발견했다. 저자는 가짜와 실제 뉴스 예제 태그가 붙은 데이터 세트로 저장소를 만들었습니다. 나는 결과를 쉽게 재현할 수 있을지, 그리고 그 모델이 무엇을 배웠는지 판단할 수 있을지 궁금했다.

이 자습서에서 당신은 내가 처음에 탐구한 몇 가지를 함께 살펴보고, 가짜 뉴스 탐지기를 성공적으로 구축할 수 있는지 알아보게 될 것이다!

팁: 자연어 처리(NLP) 기본에 대해 더 알고 싶다면 파이썬 과정에서 자연어 처리 기본 사항을 고려하십시오.

데이터 탐색

먼저 데이터를 신속히 살펴보고 내용에 대한 느낌을 받아야 한다. 그렇게 하려면 팬더 데이터 프레임을 사용하고 필요한 변환을 모양, 머리 및 적용합니다.

# Import `fake_or_real_news.csv` 
df = pd.read_csv("https://s3.amazonaws.com/assets.datacamp.com/blog_assets/fake_or_real_news.csv")

# Inspect shape of `df`
df.shape

# Print first lines of `df`
df.head()

# Set index
df = df.set_index("Unnamed: 0")

# Print first lines of `df`
df.head()

교육 데이터 추출

이제 데이터 프레임이 필요한 것에 더 가까워 보이므로 레이블을 구분하고 교육 및 테스트 데이터 세트를 설정해야 합니다.

이 공책에서는 더 긴 기사 텍스트를 사용하는 데 집중하기로 결정했습니다. 내가 bag-of-words와 용어 Term Frequency-Inverse Document Frequency(TF-IDF)를 이용해서 특징을 추출할 것임을 알았기 때문에, 이것은 좋은 선택인 것 같았다. 더 긴 텍스트를 사용하면 실제 및 가짜 뉴스 데이터에 대한 고유한 단어와 기능을 사용할 수 있습니다.

# Set `y` 
y = df.label

# Drop the `label` column
df.drop("label", axis=1)

# Make training and test sets
X_train, X_test, y_train, y_test = train_test_split(df['text'], y, test_size=0.33, random_state=53)

Building Vectorizer Classifiers

이제 교육 및 테스트 데이터를 사용하므로 분류자를 작성할 수 있습니다. 기사의 단어와 토큰이 뉴스가 가짜인지 실제인지에 중요한 영향을 미쳤는지 좋은 아이디어를 얻으려면 CountVectorizerTfidfVectorizer를 사용하는 것으로 시작합니다.

이 예제에는 max_df 인수를 사용하여 TF-IDF 벡터 라이저 tfidf_vectorizer의 최대 임계값이 .7로 설정되어 있습니다. 이렇게하면 기사의 70% 이상에 나타나는 단어가 제거됩니다. 또한 내장된 stop_words 매개 변수는 벡터를 만들기 전에 데이터에서 영어 중지 단어를 제거합니다.

사용할 수 있는 매개 변수가 더 많으며 TfidfVectorizerCountVectorizer에 대한 scikit-learn 설명서에서 모든 매개 변수를 읽을 수 있습니다.

# Initialize the `count_vectorizer` 
count_vectorizer = CountVectorizer(stop_words='english')

# Fit and transform the training data
count_train = count_vectorizer.fit_transform(X_train)

# Transform the test set
count_test = count_vectorizer.transform(X_test)

# Initialize the `tfidf_vectorizer`
tfidf_vectorizer = TfidfVectorizer(stop_words='english', max_df=0.7)

# Fit and transform the training data
tfidf_train = tfidf_vectorizer.fit_transform(X_train)

# Transform the test set
tfidf_test = tfidf_vectorizer.transform(X_test)

이제 벡터가 있으므로 count_vectorizertfidf_vectorizer에 저장된 벡터 기능을 살펴볼 수 있습니다.

눈에 띄는 문제가 있는가? (예!)

사용 중인 데이터 집합에는 주석, 측정 또는 기타 무의미한 단어와 다국어 기사가 있습니다. 보통은, 이걸 미리 처리하고 잡음을 제거하는데 더 많은 시간을 쓰고 싶을거야 하지만 이 자습서에는 작은 개념 증명이 나와 있기 때문에, 이 모델이 소음을 극복하고 이러한 문제에도 불구하고 제대로 분류할 수 있는지를 알 수 있을 것이다.

# Get the feature names of `tfidf_vectorizer` 
print(tfidf_vectorizer.get_feature_names()[-10:])

# Get the feature names of `count_vectorizer`
print(count_vectorizer.get_feature_names()[:10])

Intermezzo: Count versus TF-IDF Features

나는 내 수와 TF-IDF 벡터라이저가 다른 토큰을 추출했는지 궁금했다. 피쳐를 확인하고 비교하려면 벡터 정보를 DataFrame으로 다시 추출하여 쉬운 파이썬 비교를 사용할 수 있습니다.

아래 셀을 실행하여 볼 수 있듯이 두 벡터 라이저는 동일한 토큰을 추출했지만 분명히 다른 가중치를가집니다. 아마도 TF-IDF 벡터 라이저의 max_dfmin_df를 변경하면 결과를 변경하고 각기 다른 기능을 얻을 수 있습니다.

count_df = pd.DataFrame(count_train.A, columns=count_vectorizer.get_feature_names())

tfidf_df = pd.DataFrame(tfidf_train.A, columns=tfidf_vectorizer.get_feature_names())

difference = set(count_df.columns) - set(tfidf_df.columns)
difference

set()

print(count_df.equals(tfidf_df))


False


count_df.head()

000000000000000310000350000600010001pt000ft000km...حلبعربيعنلممامحاولاتمنهذاوالمرضىยงade
00000000000...0000000000
10000000000...0000000000
20000000000...0000000000
30000000000...0000000000
40000000000...0000000000

5 rows × 56922 columns

tfidf_df.head()

000000000000000310000350000600010001pt000ft000km...حلبعربيعنلممامحاولاتمنهذاوالمرضىยงade
00.00.00.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
10.00.00.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
20.00.00.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
30.00.00.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
40.00.00.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0

5 rows × 56922 columns

Comparing Models

이제 당신의 모델을 훈련하고 시험할 때입니다.

여기서, 여러분은 NLP가 가장 좋아하는 MultinomialNB로 시작할 것입니다. 이 도구를 사용하여 TF-IDF와 bag-of-words을 비교할 수 있습니다. 내 직감은 ‘bag-of-words’(일명 ‘카운트벡터라이저’)가 이 모델에서 더 잘 수행될 것이라는 것이었다. (다항식 분포에 대한 자세한 내용과 정수와 가장 잘 맞는 이유를 보려면 UPenn 통계 과정의이 간결한 설명을 확인하십시오.)

나는 개인적으로 혼돈 매트릭스를 비교하고 읽기가 더 쉽다고 생각한다. 그래서 나는 scikit-learn을 통해 쉽게 읽을 수 있는 혼동 행렬을 만들었다(thanks open source!). 혼란 행렬은 주 대각선 (왼쪽에서 오른쪽 아래)에 적절한 레이블을 보여줍니다. 다른 셀은 잘못된 레이블을 보여 주며, 종종 잘못된 양성 또는 거짓 네거티브라고합니다. 문제에 따라, 이 중 하나가 더 중요할 수 있습니다. 예를 들어, 가짜 뉴스 문제에 대해서는 실제 뉴스 기사를 가짜 뉴스로 분류하지 않는 것이 더 중요합니까? 만약 그렇다면, 우리는 결국 이 관심사를 더 잘 반영하기 위해 우리의 정확도 점수를 평가하고 싶어할지도 모른다.

혼란 매트릭스 외에, scikit-learn에는 모델을 시각화하고 비교하는 여러 가지 방법이 있습니다. 한 가지 일반적인 방법은 ROC 곡선을 사용하는 것입니다. scikit-learn 메트릭 모듈에서 사용할 수 있는 모델을 평가하는 다른 여러 가지 방법이 있습니다.


def plot_confusion_matrix(cm, classes,
                         normalize=False,
                         title='Confusion matrix',
                         cmap=plt.cm.Blues):
   """
  See full source and example:
  http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html
   
  This function prints and plots the confusion matrix.
  Normalization can be applied by setting `normalize=True`.
  """
   plt.imshow(cm, interpolation='nearest', cmap=cmap)
   plt.title(title)
   plt.colorbar()
   tick_marks = np.arange(len(classes))
   plt.xticks(tick_marks, classes, rotation=45)
   plt.yticks(tick_marks, classes)

   if normalize:
       cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
       print("Normalized confusion matrix")
   else:
       print('Confusion matrix, without normalization')

   thresh = cm.max() / 2.
   for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
       plt.text(j, i, cm[i, j],
                horizontalalignment="center",
                color="white" if cm[i, j] > thresh else "black")

   plt.tight_layout()
   plt.ylabel('True label')
   plt.xlabel('Predicted label')

clf = MultinomialNB()

clf.fit(tfidf_train, y_train)
pred = clf.predict(tfidf_test)
score = metrics.accuracy_score(y_test, pred)
print("accuracy:   %0.3f" % score)
cm = metrics.confusion_matrix(y_test, pred, labels=['FAKE', 'REAL'])
plot_confusion_matrix(cm, classes=['FAKE', 'REAL'])


accuracy:   0.857
Confusion matrix, without normalization


clf = MultinomialNB()

clf.fit(count_train, y_train)
pred = clf.predict(count_test)
score = metrics.accuracy_score(y_test, pred)
print("accuracy:   %0.3f" % score)
cm = metrics.confusion_matrix(y_test, pred, labels=['FAKE', 'REAL'])
plot_confusion_matrix(cm, classes=['FAKE', 'REAL'])


accuracy:   0.893
Confusion matrix, without normalization

그리고 실제로 매개 변수 조정이 전혀 없기 때문에 카운트 벡터화된 교육 세트 count_train은 TF-IDF 벡터보다 눈에 띄게 뛰어납니다!

Testing Linear Models

선형 모델이 TF-IDF 벡터라이저와 어떻게 잘 작동하는지에 대한 많은 훌륭한 설명이 있습니다 (분류를위한 word2vec, scikit-learn 텍스트 분석의 SVM 참조 등을 살펴 보십시오).

그럼 SVM을 사용해야 하는 거군요?

최근에 빅터 라브렌코의 텍스트 분류 강의를 봤는데 패시브 공격 분류기와 선형 SVM을 비교해서 텍스트 분류를 했어요. 우리는 가짜 뉴스 데이터 세트로 이 접근법 (약간의 상당한 속도 이점과 영구적인 학습 단점이 있음)을 테스트 할 것입니다.

linear_clf = PassiveAggressiveClassifier(n_iter=50)

linear_clf.fit(tfidf_train, y_train)
pred = linear_clf.predict(tfidf_test)
score = metrics.accuracy_score(y_test, pred)
print("accuracy:   %0.3f" % score)
cm = metrics.confusion_matrix(y_test, pred, labels=['FAKE', 'REAL'])
plot_confusion_matrix(cm, classes=['FAKE', 'REAL'])


accuracy:   0.936
Confusion matrix, without normalization

와!

인상 깊었어요. 혼란 매트릭스는 다르게 보이고 모델은 가짜 뉴스를 조금 더 잘 분류합니다. MultinomialNB의 alpha 값을 조정하면 비슷한 결과를 얻을 수 있는지 테스트할 수 있습니다. 또한 보다 철저한 검색을 위해 그리드 검색을 사용하여 매개 변수 조정을 사용할 수 있습니다.


clf = MultinomialNB(alpha=0.1)

last_score = 0
for alpha in np.arange(0,1,.1):
   nb_classifier = MultinomialNB(alpha=alpha)
   nb_classifier.fit(tfidf_train, y_train)
   pred = nb_classifier.predict(tfidf_test)
   score = metrics.accuracy_score(y_test, pred)
   if score > last_score:
       clf = nb_classifier
   print("Alpha: {:.2f} Score: {:.5f}".format(alpha, score))


Alpha: 0.00 Score: 0.61502
Alpha: 0.10 Score: 0.89766
Alpha: 0.20 Score: 0.89383
Alpha: 0.30 Score: 0.89000
Alpha: 0.40 Score: 0.88570
Alpha: 0.50 Score: 0.88427
Alpha: 0.60 Score: 0.87470
Alpha: 0.70 Score: 0.87040
Alpha: 0.80 Score: 0.86609
Alpha: 0.90 Score: 0.85892

별로...... 이 시점에서, 모든 분류기를 매개변수 조정하는 작업을 수행하거나, 다른 scikit-learn 학습 베이지안 분류기를 살펴보는 것이 재미있을 수 있습니다. 또한 지원 벡터 머신 (SVM)으로 테스트하여 패시브 공격성 분류기를 능가하는지 확인할 수 있습니다.

하지만 나는 패시브 어그레시브 모델이 실제로 무엇을 배웠는지에 대해 조금 더 궁금하다. 자, 이제 내성적인 관점으로 넘어가 봅시다.

Introspecting models

그럼 가짜 뉴스는 해결됐군요, 그렇죠? 제 데이터 세트에서 93%의 정확도를 얻었으니 모두 가게를 닫고 집으로 가시죠.

물론, 별로. 우리가 그 특징에서 얼마나 많은 소음을 보았는지 생각하면 나는 이러한 결과에 기껏해야 조심스럽다. StackOverflow에는 레이블에 가장 큰 영향을 미치는 벡터를 찾는 데 매우 유용한 함수가 있습니다. 이진 분류기 (2가지 클래스로 분류)에서만 작동하지만 FAKE 또는 REAL 레이블 만 있기 때문에 좋은 소식입니다.

TF-IDF 벡터 데이터 세트 (tfidf_vectorizer) 및 Passive Aggressive 분류기 (linear_clf)로 최상의 성능을 발휘하는 분류기를 사용하여 가짜 및 실제 뉴스에 대한 상위 30 개 벡터를 검사합니다.

def most_informative_feature_for_binary_classification(vectorizer, classifier, n=100):
   """
  See: https://stackoverflow.com/a/26980472
   
  Identify most important features if given a vectorizer and binary classifier. Set n to the number
  of weighted features you would like to show. (Note: current implementation merely prints and does not
  return top classes.)
  """

   class_labels = classifier.classes_
   feature_names = vectorizer.get_feature_names()
   topn_class1 = sorted(zip(classifier.coef_[0], feature_names))[:n]
   topn_class2 = sorted(zip(classifier.coef_[0], feature_names))[-n:]

   for coef, feat in topn_class1:
       print(class_labels[0], coef, feat)

   print()

   for coef, feat in reversed(topn_class2):
       print(class_labels[1], coef, feat)


most_informative_feature_for_binary_classification(tfidf_vectorizer, linear_clf, n=30)


FAKE -4.86382369883 2016
FAKE -4.13847157932 hillary
FAKE -3.98994974843 october
FAKE -3.10552662226 share
FAKE -2.99713810694 november
FAKE -2.9150746075 article
FAKE -2.54532100449 print
FAKE -2.47115243995 advertisement
FAKE -2.35915304509 source
FAKE -2.31585837413 email
FAKE -2.27985826579 election
FAKE -2.2736680857 oct
FAKE -2.25253568246 war
FAKE -2.19663276969 mosul
FAKE -2.17921304122 podesta
FAKE -1.99361009573 nov
FAKE -1.98662624907 com
FAKE -1.9452527887 establishment
FAKE -1.86869495684 corporate
FAKE -1.84166664376 wikileaks
FAKE -1.7936566878 26
FAKE -1.75686475396 donald
FAKE -1.74951154055 snip
FAKE -1.73298170472 mainstream
FAKE -1.71365596627 uk
FAKE -1.70917804969 ayotte
FAKE -1.70781651904 entire
FAKE -1.68272667818 jewish
FAKE -1.65334397724 youtube
FAKE -1.6241703128 pipeline

REAL 4.78064061698 said
REAL 2.68703967567 tuesday
REAL 2.48309800829 gop
REAL 2.45710670245 islamic
REAL 2.44326123901 says
REAL 2.29424417889 cruz
REAL 2.29144842597 marriage
REAL 2.20500735471 candidates
REAL 2.19136552672 conservative
REAL 2.18030834903 monday
REAL 2.05688105375 attacks
REAL 2.03476457362 rush
REAL 1.9954523319 continue
REAL 1.97002430576 friday
REAL 1.95034103105 convention
REAL 1.94620720989 sen
REAL 1.91185661202 jobs
REAL 1.87501303774 debate
REAL 1.84059602241 presumptive
REAL 1.80111133252 say
REAL 1.80027216061 sunday
REAL 1.79650823765 march
REAL 1.79229792108 paris
REAL 1.74587899553 security
REAL 1.69585506276 conservatives
REAL 1.68860503431 recounts
REAL 1.67424302821 deal
REAL 1.67343398121 campaign
REAL 1.66148582079 fox
REAL 1.61425630518 attack

또한 계수를 피쳐에 맞추고 목록의 맨 위와 맨 아래를 살펴봄으로써 몇 줄의 파이썬으로 아주 분명한 방식으로도 이 작업을 수행할 수 있다.


feature_names = tfidf_vectorizer.get_feature_names()

### Most real
sorted(zip(clf.coef_[0], feature_names), reverse=True)[:20]


[(-6.2573612147015822, 'trump'),
(-6.4944530943126777, 'said'),
(-6.6539784739838845, 'clinton'),
(-7.0379446628670728, 'obama'),
(-7.1465399833812278, 'sanders'),
(-7.2153760086475112, 'president'),
(-7.2665628057416169, 'campaign'),
(-7.2875931446681514, 'republican'),
(-7.3411184585990643, 'state'),
(-7.3413571102479054, 'cruz'),
(-7.3783124419854254, 'party'),
(-7.4468806724578904, 'new'),
(-7.4762888011545883, 'people'),
(-7.547225599514773, 'percent'),
(-7.5553074094582335, 'bush'),
(-7.5801506339098932, 'republicans'),
(-7.5855405012652435, 'house'),
(-7.6344781725203141, 'voters'),
(-7.6484824436952987, 'rubio'),
(-7.6734836186463795, 'states')]

### Most fake
sorted(zip(clf.coef_[0], feature_names))[:20]


[(-11.349866225220305, '0000'),
(-11.349866225220305, '000035'),
(-11.349866225220305, '0001'),
(-11.349866225220305, '0001pt'),
(-11.349866225220305, '000km'),
(-11.349866225220305, '0011'),
(-11.349866225220305, '006s'),
(-11.349866225220305, '007'),
(-11.349866225220305, '007s'),
(-11.349866225220305, '008s'),
(-11.349866225220305, '0099'),
(-11.349866225220305, '00am'),
(-11.349866225220305, '00p'),
(-11.349866225220305, '00pm'),
(-11.349866225220305, '014'),
(-11.349866225220305, '015'),
(-11.349866225220305, '018'),
(-11.349866225220305, '01am'),
(-11.349866225220305, '020'),
(-11.349866225220305, '023')]

따라서 정치적 의도와 근원을 가장 가짜로 표현할 수 있는 특정 단어가 분명히 존재한다(예를 들어 기업이나 설립).

또한 실제 뉴스 데이터는 신문과 대부분의 저널리즘 출판물 출처가 직접 인용되기 때문에 "말하기"동사의 형태를 더 자주 사용합니다 ( "독일 총리 앙겔라 메르켈은 ...").

현재 분류자에서 전체 목록을 추출하고 각 토큰을 살펴보려면(또는 분류자와 분류자 사이의 토큰을 쉽게 비교할 수 있다) 쉽게 내보낼 수 있다.

tokens_with_weights = sorted(list(zip(feature_names, clf.coef_[0])))

Intermezzo: HashingVectorizer

텍스트 분류에 때때로 사용되는 또 다른 벡터화 기는 HashingVectorizer입니다. HashingVectorizer는 메모리가 적고 더 빠르지만 (토큰보다는 해시를 사용하기 때문에) 내성이 더 어렵습니다. 관심이 있다면 HashingVectorizer를 scikit-learn 문서에 사용하는 방법과 단점에 대해 좀 더 자세히 읽어볼 수 있다.

결과를 다른 벡터 라이저와 비교하고 시도 할 수 있습니다. MultinomialNB를 사용하는 TF-IDF 벡터 라이저보다 훨씬 우수한 성능을 보였지만 (수동형 Ag가있는 TF-IDF 벡터 라이저와 CountVectorizer가 더 잘 수행되는 이유와 동일한 이유로 인해 다소 예상됩니다)


hash_vectorizer = HashingVectorizer(stop_words='english', non_negative=True)
hash_train = hash_vectorizer.fit_transform(X_train)
hash_test = hash_vectorizer.transform(X_test)

clf = MultinomialNB(alpha=.01)

clf.fit(hash_train, y_train)
pred = clf.predict(hash_test)
score = metrics.accuracy_score(y_test, pred)
print("accuracy:   %0.3f" % score)
cm = metrics.confusion_matrix(y_test, pred, labels=['FAKE', 'REAL'])
plot_confusion_matrix(cm, classes=['FAKE', 'REAL'])


accuracy:   0.902
Confusion matrix, without normalization

clf = PassiveAggressiveClassifier(n_iter=50)

clf.fit(hash_train, y_train)
pred = clf.predict(hash_test)
score = metrics.accuracy_score(y_test, pred)
print("accuracy:   %0.3f" % score)
cm = metrics.confusion_matrix(y_test, pred, labels=['FAKE', 'REAL'])
plot_confusion_matrix(cm, classes=['FAKE', 'REAL'])


accuracy:   0.921
Confusion matrix, without normalization

Conclusion

그럼 가짜 뉴스 분류기 실험이 성공했나요? 확실히 아니죠.

하지만 새로운 데이터 세트를 가지고 놀고, NLP 분류 모델을 테스트하고, 얼마나 성공했는지 내성적으로 생각하게 되었습니까? 네.

처음부터 예상대로 간단한 bag-of-words 또는 TF-IDF 벡터로 가짜 뉴스를 정의하는 것은 지나치게 단순화된 접근 방식입니다. 특히 시끄러운 토큰으로 가득 찬 다국어 데이터 세트를 사용하면 더욱 그렇습니다. 만약 여러분이 여러분의 모델이 실제로 무엇을 배웠는지 보지 않았다면, 여러분은 그 모델이 의미 있는 것을 배웠다고 생각했을 것입니다. 따라서 기억하십시오 : 항상 모델을 내성적으로 생각하십시오 (최선을 다해!).

제가 놓친 데이터에서 다른 추세를 발견하셨다면 궁금하군요. 나는 내 블로그에서 중요한 특징들을 비교하는 다른 분류기들에 대한 게시물을 추적할 것이다. 흥미로운 것을 연구하고 찾는 데 시간을 할애하면 의견에서 발견 및 메모를 자유롭게 공유하거나 항상 트위터 (@kjam)에 연락할 수 있습니다.

저와 함께 새로운 NLP 데이터 세트를 탐색하는 재미를 좀 보셨으면 좋겠네요!