김예빈Yebeen Kim

포트폴리오 · 학부 개인 프로젝트 · 사회연결망분석과 텍스트마이닝

위로 챗봇 '포근이' — 고민을 39개로 나눴지만, 앱에 들어간 분기는 3개였다

네이트판 '힘들어' 검색 결과를 크롤링해 고민 글에 손으로 범주를 붙이고, 범주별 TF-IDF 핵심어를 뽑은 뒤 안드로이드 앱까지 혼자 만든 학부 개인 프로젝트

기간
2019.09 – 2019.12
역할
개인 프로젝트 — 문제 정의, 크롤링, 라벨링, 전처리·TF-IDF 분석, 안드로이드 앱 구현까지 전 과정
발표·산출
사회연결망분석과 텍스트마이닝 프로젝트 발표 (한양대학교 산업공학과)
날짜
2019.10
목차 15개 절
목차
  1. 왜 만들었나 — 위로는 아무 말이나 하면 안 된다
  2. 수집원을 고른 근거 — 조사 리포트 한 장
  3. ‘힘들어’를 필터로 쓰고, 모자란 주제는 다른 사이트로 채웠다
  4. 수집을 두 단계로 나눴다 — 목록에서 링크, 그다음 본문
  5. 39개 카테고리 — 손으로 붙인 라벨
  6. 전처리 — 무엇을 문서로 볼지부터 정했다
  7. TF-IDF — ‘상위 100개’의 실제 의미
  8. t-SNE로 펼쳐 봤지만, 군집은 보이지 않았다
  9. 범주별 핵심어를 읽어 보면
  10. 위로 문구의 근거 — 논문 한 편과 책 한 권
  11. 챗봇 모델 선택 — retrieval인가 generative인가
  12. 구현 — 앱 안에 실제로 들어간 것
  13. 완성된 화면 — 기본 응답이 대화를 이끈다
  14. 남은 것 — 분석과 앱 사이의 간격
  15. 한 번 만든 파이프라인을 다른 서비스에 그대로 썼다
요약
  • “위로는 아무 말이나 하면 안 된다”는 문제의식에서 출발했다. 맞는 위로를 건네려면 먼저 그 고민이 어떤 종류인지 알아야 한다고 보고, 문제를 ‘고민 분류’와 ‘위로 문구 선정’ 두 단계로 쪼갰다.
  • 수집원은 감으로 고르지 않았다. 대학내일20대연구소의 2016년 대학생 온라인커뮤니티 이용실태조사에서 네이트판이 1위(8.5%)로 나온 표를 근거로 네이트판을 골랐고, 정신의학신문 ‘무엇이든 물어보세요’ 게시판을 두 번째 수집원으로 더했다. 네이트판에서는 게시판을 통째로 긁는 대신 ‘힘들어’라는 검색어를 고민 글 필터로 썼다.
  • 수집은 목록에서 글 링크를 모으는 단계와 본문을 긁는 단계로 나눴고, 모은 글은 표 계산기에서 손으로 범주를 붙였다. 슬라이드는 39개 카테고리라고 적었지만 실제로 나열된 것은 38개이고, ‘우울증’과 ‘우울’, ‘연애 지침’과 ‘연인관계 지침’처럼 겹치는 범주가 섞여 있다.
  • 전처리는 형태소 분석기로 명사·동사·형용사만 남기고 어간을 통합한 뒤 직접 만든 불용어 목록을 적용했다. 다만 실제로 남아 있는 불용어 목록은 17개(그중 ‘때문’은 중복)로, ‘사전’이라 부르기에는 작다.
  • TF-IDF로 범주별 어휘를 뽑고 그 결과를 t-SNE 2차원으로 펼쳐 봤다. 그러나 t-SNE 그림에서 의미 있는 군집은 보이지 않았고, 실제로 쓴 산출물은 그림이 아니라 범주별 단어 목록이었다.
  • 안드로이드 앱을 만들어 실기기에 설치까지 했다. 하지만 앱 안에 들어간 것은 TF-IDF 파이프라인이 아니라 입력에 특정 키워드가 들어 있는지 검사하는 세 갈래 분기와 기본 응답 하나였다. 분석에서 만든 39개 범주 중 앱이 실제로 구분한 것은 세 갈래뿐이다.
주요 수치
298,149건네이트판 '힘들어' 검색 결과크롤링 시점(2019.12.07)의 톡톡 검색 결과 총 건수. 이 중 일부만 수집했다.
6,300본문 수집 루프저장한 게시글 링크 목록을 도는 for i in range(6300). 실제 성공 수집 건수는 자료에 없다.
39개 (실제 나열 38개)고민 분류 체계슬라이드 표기는 39개지만 나열된 범주명은 38개이고 서로 겹치는 항목이 있다.
max_features = 100 / 20TF-IDF 어휘 상한max_df=0.95, min_df=0. 'Top 100'·'Top 20'은 이 상한으로 남긴 어휘 집합이다.
3 + 기본응답 1앱에 구현된 분기ChatActivity.java의 if-else. 39개 범주가 앱에서는 세 갈래로 줄었다.

왜 만들었나 — 위로는 아무 말이나 하면 안 된다

발표 자료의 첫 장은 기술이 아니라 통계로 시작한다.

발표 자료는 방법이 아니라 숫자로 시작한다. 세 번째 슬라이드에 적힌 것은 다음과 같다.

  • 13년째 자살률 OECD 1위
  • 하루 평균 36명, 40분마다 1명이 자살로 생을 마감
  • 청소년·청년층 사망 원인의 1위가 자살
  • 전체 사망 원인 중 자살이 차지하는 비율 — 10~19세 30.9%, 20~29세 44.8%, 30~39세 36.9%

출처는 연합뉴스 기사와 박상휘 기자의 기사로 적혀 있다. 그다음 슬라이드에서 만들려는 것을 한 문장으로 정의했다. 정서적으로 내 편이 필요한 사람들에게 공감과 따뜻한 위로 한마디.

여기서 문제가 갈라진다. 위로를 건네려면 먼저 무엇 때문에 힘든지를 알아야 한다. 그래서 프로젝트를 두 단계로 쪼갰다. 첫째, 사람이 쓴 고민 글이 어떤 종류의 고민인지 알아맞히는 것. 둘째, 그 종류에 맞는 위로 문장을 무슨 근거로 고를 것인가. 첫 번째가 텍스트 분류 문제이고, 두 번째는 문헌 조사 문제였다.

수업은 사회연결망분석과 텍스트마이닝이었고 이 프로젝트는 개인 과제였다. 문제 정의, 크롤링, 라벨링, 분석, 안드로이드 앱 구현까지 전 과정이 한 사람의 손을 거쳤다. 발표 자료에 적어 둔 작업 순서는 웹크롤링 → 데이터셋 모으기 → 데이터셋 분석 → 데이터셋 전처리 → 데이터셋 분류 → 텍스트마이닝 → 앱 개발이다.

표지 카피부터 웹크롤링·전처리·TF-IDF 분석·위로 문구 선정·완성 대화 화면까지, 프로젝트 전체 흐름을 한 장에 모은 원본 자료.
표지 카피부터 웹크롤링·전처리·TF-IDF 분석·위로 문구 선정·완성 대화 화면까지, 프로젝트 전체 흐름을 한 장에 모은 원본 자료.

수집원을 고른 근거 — 조사 리포트 한 장

“고민이 많이 올라오는 게시판”을 감으로 고르지 않았다.

고민 글은 정제된 데이터셋으로 존재하지 않는다. 사람들이 실제로 고민을 털어놓는 곳을 직접 찾아야 했고, 그 선택의 근거로 [대학내일20대연구소] 2016 전국 대학생 온라인커뮤니티 이용실태조사(연구리포트 2016-08)의 ‘주1회 이상 이용하는 온라인 커뮤니티 순위’ 표를 슬라이드에 붙였다(n=497, 복수응답 r=589).

순위이용채널비율
1네이트판8.5%
2인벤4.9%
3디씨인사이드4.4%
4쭉빵카페4.1%
5여성시대3.1%
6파우더룸2.9%
7인스티즈2.0%
8오늘의유머2.0%
9아이러브사커1.9%
10뽐뿌1.7%

1위인 네이트판을 주 수집원으로 정했다. 여기에 두 번째 수집원으로 정신의학신문‘무엇이든 물어보세요’ 게시판을 더했다. 앞의 것은 익명 게시판의 구어체 고민 글이고, 뒤의 것은 상담을 목적으로 올라오는 글이라 성격이 다르다.

다만 인용한 조사는 대학생의 커뮤니티 이용 실태이고, 만들려는 앱의 대상은 대학생만이 아니다. 이 표는 ‘어디에 고민 글이 많은가’가 아니라 ‘어디를 많이 쓰는가’를 보여 준다는 점에서 대리 지표에 가깝다.

네이트판을 수집원으로 고른 근거로 인용한 대학생 온라인커뮤니티 이용 순위표 — 네이트판이 8.5%로 1위이고 2위 인벤(4.9%)과 차이가 크다.
네이트판을 수집원으로 고른 근거로 인용한 대학생 온라인커뮤니티 이용 순위표 — 네이트판이 8.5%로 1위이고 2위 인벤(4.9%)과 차이가 크다.

‘힘들어’를 필터로 쓰고, 모자란 주제는 다른 사이트로 채웠다

게시판을 통째로 긁는 대신 검색어 하나로 고민 글을 골라냈다.

네이트판은 고민만 올라오는 게시판이 아니다. 통째로 긁으면 고민이 아닌 글이 대부분이 된다. 그래서 ‘힘들어’라는 검색어를 고민 글을 골라내는 필터로 썼다. 슬라이드의 표현은 이렇다 — “‘힘들어’로 검색한 결과 크롤링 …> 다양한 우울한 이유 및 고민 텍스트 수집”.

검색어 하나로 여러 종류의 고민을 한꺼번에 확보할 수 있다는 것이 이 방식의 이점이다. 실제 검색 결과 화면을 보면 고3 입시 고민, 친구 관계에서 오는 위축감, 돈 문제, 친구와의 다툼이 한 화면에 섞여 나온다. 검색 결과 화면에 찍힌 게시판 검색 결과 총 건수는 298,149개였고, 글에 붙은 날짜는 2019년 12월 7일이다.

정신의학신문 쪽은 검색 없이 ‘무엇이든 물어보세요’ 게시글 목록을 그대로 수집했다. 캡처에 남은 글 번호는 1159~1166번이고 작성일은 2019년 11월 18~20일이다. 제목이 “우울증 진단 받아보고 싶습니다”, “속마음을 얘기할 수 없어요”, “특정 기억과 함께 얼어붙는듯한 고통”처럼 처음부터 고민으로 쓰인 글이라 필터가 따로 필요 없었다.

여기서 정직하게 남는 한계가 있다. ‘힘들어’라는 표현을 쓰지 않은 고민 글은 처음부터 수집 대상에서 빠진다. 검색 결과 29만여 건 중 실제로 수집한 것은 일부이고, 그 표본이 고민 전체를 대표한다는 근거는 자료에 없다.

수집을 마치고 나니 문제가 하나 더 드러났다. 고민 종류마다 모이는 텍스트 양의 편차가 컸다. 슬라이드에 적힌 세 번째 항목이 그 대응이다 — “각 고민들에 대해 텍스트 양이 불충분한 경우 다른 사이트에서 추가 크롤링”. 예로 적어 둔 고민은 가정폭력이고, 링크는 법제처 찾기쉬운 생활법령정보의 생활법령 안내 페이지다. 뒤에 ‘등등’이 붙어 있어 여러 사이트를 썼음을 알 수 있지만 어떤 사이트를 더 썼는지는 적혀 있지 않다.

게시판에서 문장이 몇 개 나오지 않는 주제, 즉 가정폭력처럼 사람들이 길게 털어놓지 않는 고민에 대해서는 같은 주제를 다루는 공공 정보 페이지까지 끌어와 그 범주의 어휘를 확보한 셈이다. 다만 이 방식에는 대가가 있다. 법령 안내문의 문체는 고민 게시글의 문체와 전혀 달라서, 그 범주의 핵심어에 사람이 실제로 쓰는 말 대신 제도 용어가 섞여 들어간다. 뒤에 나오는 가정폭력 범주의 상위 단어에 ‘이혼’·‘신고’·‘지원’ 같은 말이 함께 잡히는 것이 그 흔적이다.

네이트판에서 '힘들어'로 검색한 결과 화면 — 총 298,149개가 잡히고, 성적·친구 관계·돈 문제 등 서로 다른 이유의 고민이 한 화면에 섞여 나온다.
네이트판에서 '힘들어'로 검색한 결과 화면 — 총 298,149개가 잡히고, 성적·친구 관계·돈 문제 등 서로 다른 이유의 고민이 한 화면에 섞여 나온다.
두 번째 수집원인 정신의학신문 '무엇이든 물어보세요' 게시글 목록 — 2019년 11월 중순의 글 번호와 조회수가 그대로 찍혀 있다.
두 번째 수집원인 정신의학신문 '무엇이든 물어보세요' 게시글 목록 — 2019년 11월 중순의 글 번호와 조회수가 그대로 찍혀 있다.

수집을 두 단계로 나눴다 — 목록에서 링크, 그다음 본문

링크 수집과 본문 수집을 나눠 처리했고, 그렇게 나눈 이유가 있다.

슬라이드에는 수집을 두 단계로 적어 두었다. 게시글 목록 페이지에서 각 페이지를 받아 오는 단계, 그리고 그렇게 모은 페이지에서 게시글 본문 텍스트를 긁는 단계다. 두 단계는 서로 다른 방식으로 짰다. 목록은 정적인 HTML만 받아 링크를 뽑으면 충분했고, 본문은 브라우저를 띄워야 텍스트가 나왔다.

목록 순회는 검색 결과의 페이지 번호를 바꿔 가며 도는 방식이고, 캡처에서 확인되는 범위는 21페이지까지다. 뽑아낸 링크는 파일로 떨어뜨려 두었고, 본문 수집은 그 파일을 읽어 링크 목록을 다시 만드는 데서 시작한다. 두 단계를 한 번에 붙여 돌리지 않은 대신, 본문 수집을 언제든 다시 돌릴 수 있게 된 구조다. 다만 목록에서 뽑은 링크를 파일로 옮긴 중간 단계는 캡처에 남아 있지 않다.

본문 수집은 링크 목록을 6,300건까지 도는 것으로 작성되어 있고, 개별 글에서 예외가 나면 건너뛴다. 실패한 건수와 최종적으로 몇 건이 남았는지는 자료에 기록되어 있지 않다.

목록 페이지 수집 — 검색 결과의 페이지 번호를 바꿔 가며 받아, 목록에서 게시글 링크만 뽑아낸다.
목록 페이지 수집 — 검색 결과의 페이지 번호를 바꿔 가며 받아, 목록에서 게시글 링크만 뽑아낸다.
본문 수집 — 저장해 둔 링크 목록을 6,300건까지 순회하며 게시글 본문 텍스트를 수집하고, 실패한 글은 건너뛴다.
본문 수집 — 저장해 둔 링크 목록을 6,300건까지 순회하며 게시글 본문 텍스트를 수집하고, 실패한 글은 건너뛴다.

39개 카테고리 — 손으로 붙인 라벨

분류는 모델이 한 것이 아니라 표 계산기에서 사람이 했다.

수집한 글을 읽어 가며 고민을 39개 범주로 나눴다고 슬라이드는 적고 있다. 원본에 남은 실제 작업물은 두 개의 표다. 하나는 고민 글 원문과 붙인 범주 두 열로 된 라벨링 시트이고, 다른 하나는 그것을 원문·범주 두 열로 정리한 최종 데이터셋이다. 즉 분류는 학습한 모델이 아니라 손으로 붙인 라벨이고, 분류 정확도 같은 지표는 존재하지 않는다.

슬라이드 13에 예시로 실린 한 건은 다음과 같다.

“전여자 친구랑 헤어진 지 한달이 되가는데 아직도 너무 힘들고 진짜 보고싶다… 진짜 걔 없으면 죽을거같고 지금도 우울증 약먹고 있는데 너무 힘들어 한번만이라도 다시 보고 싶은데 뭐라고 해야돼..” → 카테고리: 이별

슬라이드 14에 나열된 범주명은 이렇다. 우울증 · 가정폭력 · 불행감 · 부모와 갈등 · 친구관계 소외감 · 학업 스트레스 · 개학 증후군 · 외로움 · 연애 지침 · 열등감 · 가난 · 이별 · 학교 부적응 · 시댁 갈등 · 친구관계 지침 · 가정불화 · 가족 자살 · 연인 갈등 · 집착 · 불효 · 상대의 바람 · 형제 갈등 · 층간소음 · 짝사랑 · 사기당함 · 힘듬 · 친구 죽음 · 회사 힘듬 · 외모 · 왕따 · 다이어트 · 부부 갈등 · 친구 갈등 · 우울 · 연인관계 지침 · 인간관계 부적응 · 가족 불행 · 학교폭력.

세어 보면 38개다. 슬라이드는 “39개의 카테고리로 분류”라고 적었으므로 하나가 누락됐거나 표기가 틀렸다. 그리고 목록 자체에도 문제가 있다.

  • 겹치는 범주 — ‘우울증’과 ‘우울’, ‘연애 지침’과 ‘연인관계 지침’, ‘가정불화’와 ‘가족 불행’, ‘친구 갈등’과 ‘친구관계 지침’은 경계가 분명하지 않다.
  • 층위가 다른 범주 — ‘힘듬’은 다른 모든 범주를 포괄할 수 있는 말인데 다른 범주와 같은 층위에 놓여 있다.
  • 목록에 없는 범주가 뒤에 등장한다 — TF-IDF 결과 슬라이드에는 ‘조부 갈등’이 13번째 범주로 나오지만 이 목록에는 없다.

범주를 정하는 일이 이 프로젝트에서 가장 결과를 좌우한 설계였는데, 그 기준을 문서로 남기지 않은 채 글을 읽으며 그때그때 늘려 간 흔적이 목록에 그대로 남아 있다.

라벨링 작업 시트 — 왼쪽 열에 고민 글 원문, 오른쪽 열에 사람이 붙인 범주(우울증·가정폭력·불행감·부모와 갈등…)가 들어 있다.
라벨링 작업 시트 — 왼쪽 열에 고민 글 원문, 오른쪽 열에 사람이 붙인 범주(우울증·가정폭력·불행감·부모와 갈등…)가 들어 있다. (세로가 긴 전체 캡처 — 상자 안에서 스크롤하거나 눌러서 크게 보세요)
최종 데이터셋 — 한 열에 고민 글 원문, 다른 열에 사람이 붙인 범주가 한 행씩 짝지어져 있고, 맞춤법도 이모티콘도 손대지 않은 원문 그대로다.
최종 데이터셋 — 한 열에 고민 글 원문, 다른 열에 사람이 붙인 범주가 한 행씩 짝지어져 있고, 맞춤법도 이모티콘도 손대지 않은 원문 그대로다.

전처리 — 무엇을 문서로 볼지부터 정했다

TF-IDF는 문서 단위를 무엇으로 잡느냐에 따라 결과가 완전히 달라진다.

슬라이드 15는 분석의 출발점을 한 줄로 못 박는다.

각 게시물 한 개를 하나의 문서로 취급 / 게시물 내 단어를 항(term)으로. 고민 글 한 건이 문서 하나가 된다.

그다음 전처리를 세 단계로 적어 두었다.

단계슬라이드 표기실제로 한 일
1)품사태깅 — 3개의 품사 추출 (원인 명사 / 감정 형용사 / 행동 동사)형태소 분석기로 품사를 붙인 뒤 명사·동사·형용사만 남김
2)어간추출같은 단계에서 어간을 통합
3)스톱워드 처리 — 불용어 사전 직접 제작직접 만든 불용어 목록에 없는 단어만 남겨 문서를 다시 조립

품사 세 가지를 고른 이유는 슬라이드에 적어 둔 대로다. 명사는 고민의 원인(무엇에 관한 고민인가), 형용사는 감정(어떤 상태로 힘든가), 동사는 행동(무엇을 했거나 하려 하는가)을 담는다고 봤다. 어간 추출을 켠 이유도 분명하다. 한국어는 어미 변화가 심해 같은 뜻의 단어가 여러 형태로 흩어지고, 이를 묶지 않으면 빈도가 잘게 쪼개져 핵심어가 드러나지 않는다.

가장 손이 많이 간 부분은 불용어였다. 기성 불용어 사전은 고민 게시판의 언어에 맞지 않았다. 다만 실제로 남아 있는 목록은 17개뿐이고, 그중 ‘때문’은 두 번 들어가 있다.

누가 · 고민 · 쓰다 · 그때 · 정말 · 진짜 · 요즘 · 이야기 · 여기 · 때문 · 우리 · 어떻다 · 수도 · 무기 · 꼬리 · 때문 · 써다

발표에서는 ‘불용어 사전 직접 제작’이라고 썼지만, 실물은 17개짜리 목록이다. ‘사전’이라 부르기에는 작고, 뒤에서 보게 될 상위 단어 목록에 ‘가다’·‘보다’·‘없다’·‘이다’처럼 아무 범주에서나 나오는 말들이 여전히 남아 있는 이유가 여기에 있다.

전처리 — 고민 글에 품사를 붙여 명사·동사·형용사만 남기고 어간을 통합한 뒤, 17개짜리 불용어 목록을 걸러 문서를 다시 만든다.
전처리 — 고민 글에 품사를 붙여 명사·동사·형용사만 남기고 어간을 통합한 뒤, 17개짜리 불용어 목록을 걸러 문서를 다시 만든다.

TF-IDF — ‘상위 100개’의 실제 의미

왜 단순 빈도가 아닌지가 원문에 예시와 함께 적혀 있다.

슬라이드 17은 TF-IDF를 쓴 이유를 세 줄로 적었다.

  • 문서 내 중요 단어를 이해하는 데 활용한다.
  • 단순 용어의 빈도가 아닌, 전체 문서에서의 출현 정도를 고려한다.
  • 따라서 여러 문서에서 사용되는 용어(“슬퍼”)는 상대적으로 낮은 가중치를 받는다.

고민 글 말뭉치에서 ‘슬퍼’는 어느 범주에나 등장하므로 그 범주를 식별하지 못한다는 뜻이다. 일반론이 아니라 자기 데이터에 실제로 있는 단어를 들어 설명한 점이 좋다.

계산은 세 가지 조건으로 걸었다. 어휘를 상위 100개(슬라이드에 따라 20개)로 제한하고, 문서의 95%를 넘게 등장하는 단어는 제외하고(위의 “슬퍼” 문제에 대한 대응), 하한은 두지 않아 한 문서에만 나오는 단어도 남겼다.

여기서 하나 바로잡을 것이 있다. 결과 슬라이드는 매번 “Tf-idf 분석결과 Top 100 결과들”, “Top 20 결과들”이라고 적었지만, 화면에 찍힌 목록은 가나다순으로 정렬된 어휘 집합이다. 더구나 어휘 상한이 단어를 고르는 기준은 TF-IDF 가중치가 아니라 말뭉치 안에서의 단순 등장 빈도다. 즉 ‘Top N’은 가중치 상위 N개가 아니라 빈도 상위 N개 어휘를 가나다순으로 늘어놓은 것이고, 어떤 단어의 가중치가 얼마였는지는 어느 슬라이드에도 나오지 않는다. 문서×단어 행렬에는 그 값이 들어 있었는데도 결과로 옮겨진 것은 어휘 목록뿐이다. 슬라이드 20이 어휘 상한을 20개로 두었을 때라고 조건을 밝혀 둔 것이 이 사실을 확인해 준다.

이 기준을 알고 나면 목록의 다른 문제도 설명된다. 가정폭력 범주의 100개 목록에는 ‘노발’·‘대발’·‘데리’처럼 형태소 분석이 잘못 잘라 낸 조각이 섞여 있다. 빈도 상위 100위 안에 이런 조각이 들어왔다는 것은 그 범주의 말뭉치가 몇 건의 글로 순위가 흔들릴 만큼 작았다는 뜻이다. 하한을 두지 않은 것도 이런 조각을 어휘에서 걸러 내지 못한 이유가 된다.

TF-IDF와 시각화 — 문서×단어 행렬을 만들고, 그 행렬을 전치해 단어를 t-SNE로 2차원에 펼친 뒤 각 점에 단어를 찍는다.
TF-IDF와 시각화 — 문서×단어 행렬을 만들고, 그 행렬을 전치해 단어를 t-SNE로 2차원에 펼친 뒤 각 점에 단어를 찍는다.

t-SNE로 펼쳐 봤지만, 군집은 보이지 않았다

표로만 보지 않고 그림으로 확인하려 한 시도와 그 결과.

TF-IDF 결과를 표로만 보지 않았다. 문서×단어 행렬을 전치해 단어를 점으로 놓고 t-SNE로 2차원에 펼친 뒤, 각 점에 단어를 적어 산점도를 그렸다.

결과 그림 두 장이 서로 다른 것을 보여 준다.

  • 단어 100개짜리 그림(우울증 범주)은 점이 화면 전체에 퍼져 있고 라벨이 서로 겹쳐 상당수가 읽히지 않는다. 왼쪽 위에 ‘강아지·성격·인간관계·남편’, 오른쪽에 ‘행복하다·감정·괜찮다·정신병원’이 모여 있는 정도는 눈에 띄지만, 이 배치가 의미를 반영한 것인지 t-SNE의 초기값 때문인지 구분할 근거가 없다.
  • 단어 20개짜리 그림(가정폭력 범주)은 라벨이 겹치지 않아 읽기는 좋지만, 점 20개가 거의 균등하게 흩어져 있어 군집이라 부를 만한 덩어리가 없다.

정직하게 말하면 t-SNE는 이 데이터에서 별 정보를 주지 못했다. t-SNE는 점 사이의 국소 이웃 관계를 보존하는 방법인데, 여기서 단어 벡터의 차원은 문서 수이고 각 범주의 문서 수가 많지 않다. 게다가 점이 20~100개뿐인 집합에서는 perplexity 기본값(30)이 점 수에 육박해 ‘국소 이웃’이라 부를 만한 것이 남지 않는다. perplexity를 따로 지정한 흔적도 없다. 실제로 이후 단계에서 쓴 산출물은 이 그림이 아니라 범주별 단어 목록 그 자체였다.

그래도 이 시도가 쓸모없지는 않았다. 그림을 그려 봤기 때문에 ‘군집이 나오지 않는다’는 것을 알았고, 범주를 자동으로 나누는 대신 사람이 라벨을 붙이는 쪽으로 간 판단이 결과적으로 맞았다.

우울증 범주의 단어 100개를 t-SNE로 펼친 그림 — 라벨이 서로 겹쳐 상당수가 읽히지 않고, 뚜렷한 군집도 보이지 않는다.
우울증 범주의 단어 100개를 t-SNE로 펼친 그림 — 라벨이 서로 겹쳐 상당수가 읽히지 않고, 뚜렷한 군집도 보이지 않는다.
가정폭력 범주를 단어 20개로 줄여 다시 그린 그림 — 단어는 읽히지만 점 20개가 고르게 흩어져 있을 뿐이다.
가정폭력 범주를 단어 20개로 줄여 다시 그린 그림 — 단어는 읽히지만 점 20개가 고르게 흩어져 있을 뿐이다.

범주별 핵심어를 읽어 보면

그림보다 목록이 더 많은 것을 말해 줬다.

실제로 쓸모가 있었던 것은 범주별 단어 목록이다. 슬라이드 18~27에 13개 범주의 결과가 실려 있고, 그중 네 개를 옮기면 다음과 같다.

범주남은 단어 (어휘 상한 20개)
가정폭력가다, 가정폭력, 돌아가다, 때리다, 맞다, 보다, 부모님, 아버지, 아빠, 어머니, 없다, 오빠, 이다, 이해, 이혼, 자다, 자식, 취업, 폭력, 한번
불행감가다, 결혼, 공부, 나르다, 동생, 모르다, 보다, 부모님, 사람, 살다, 생각, 아빠, 않다, 엄마, 없다, 오다, 우울증, 웃다, 죽다, 행복하다
부모와 갈등공부, 다니다, 독립, 들다, 보고, 부모님, 살기, 살다, 싫다, 싸우다, 아니다, 아빠, 얼굴, 엄마, 없다, 오다, 우울하다, 이번, 크다, 학교
열등감그림, 기도, 나다, 남자, 느껴지다, 다른사람, 들어가다, 마음, 문제, 미술, 발전, 버티다, 보고, 분위기, 불안하다, 사실, 안되다, 어리다, 연애, 열등감, 영어, 예쁘다, 오빠, 우울하다, 자랑, 잘나다, 전부, 절대, 주위, 회사

읽어 보면 세 가지가 보인다.

첫째, 범주를 실제로 식별하는 단어가 있다. ‘가정폭력·때리다·맞다·폭력·이혼’, ‘열등감·잘나다·자랑·예쁘다’처럼 그 범주에서만 두드러지는 단어가 남았다. 반면 ‘독립’(부모와 갈등)이나 ‘미술·영어’(열등감)처럼 특정 게시글 몇 건 때문에 올라온 것으로 보이는 단어도 섞여 있다.

둘째, 불용어가 덜 걸러졌다. ‘없다’는 네 범주 중 세 곳에, ‘가다’·‘보다’·‘오다’는 두 곳씩에 남아 있다. 어느 고민 글에나 나오는 말이라 범주를 식별하지 못하는데도 상위 목록을 차지한 것이다. 문서의 95%를 넘게 등장하는 단어만 자르는 조건으로는 이 정도로 흔한 말이 살아남는다. 17개짜리 불용어 목록으로는 부족했다는 뜻이다.

셋째, 표기가 자료 안에서 어긋난다. 열등감 슬라이드는 제목이 “Top 20 결과들”인데 실제로 나열된 단어는 30개다. 앞 절에서 본 ‘Top N’의 의미와 함께, 발표 자료의 라벨을 그대로 믿으면 안 되는 지점이다.

열등감 범주의 결과 그림 — 슬라이드 제목은 'Top 20 결과들'인데 실제로 찍힌 단어는 30개다. '열등감·잘나다·자랑·예쁘다'처럼 범주를 식별하는 말과 '미술·영어'처럼 특정 글에서만 온 것으로 보이는 말이 함께 남아 있다.
열등감 범주의 결과 그림 — 슬라이드 제목은 'Top 20 결과들'인데 실제로 찍힌 단어는 30개다. '열등감·잘나다·자랑·예쁘다'처럼 범주를 식별하는 말과 '미술·영어'처럼 특정 글에서만 온 것으로 보이는 말이 함께 남아 있다.

위로 문구의 근거 — 논문 한 편과 책 한 권

무엇이 위로가 되는지는 개발자의 감각이 아니라 연구가 이미 다룬 주제다.

분류가 끝나면 각 범주에 어떤 위로를 붙일지가 남는다. 이 부분을 임의로 쓰지 않았다. 위로하는 말은 잘못 고르면 오히려 상처가 되고, 무엇이 위로가 되는지는 이미 연구된 주제이기 때문이다.

슬라이드 30에는 근거가 명시되어 있다. “볼거 교수의 ‘보이지 않는 조언’과 정혜신 정신과의사 ‘당신이 옳다’ 도서에 따른 위로 선정”이고, 참고 문헌은 다음과 같이 정식 서지로 적혀 있다.

Bolger, N., Zuckerman, A., & Kessler, R. C. (2000). Invisible support and adjustment to stress. Journal of Personality and Social Psychology, 79, 953-961.

즉 앞의 근거는 책이 아니라 Bolger의 invisible support 연구다. 도움을 받는 사람이 ‘도움받고 있다’고 인식하지 않는 형태로 전달되는 지지가 스트레스 대처에 더 효과적이라는 개념으로, 근거의 성격이 에세이 두 권이 아니라 심리학 논문 + 상담 실무서의 조합이었다는 뜻이다.

그 근거에서 뽑아낸 문장 세 개가 슬라이드에 예시로 적혀 있다. 셋 다 ‘조언’이 아니라 지지의 형태다.

  • “나는 예전에 힘들었을 때 이렇게 하니 도움이 됐어” — 조언을 자기 경험의 형태로 감싸 전달하는 방식(invisible support)
  • “정서적으로 내 편이 필요해서..”
  • “너 옳아. 니 마음은 옳아.”와 같은 무조건적인 지지

세 번째 문장이 『당신이 옳다』의 제목과 그대로 겹치는 데서, 문헌이 문구의 표현까지 직접 규정했음을 볼 수 있다. 다만 39개 범주 각각에 어떤 문장을 배정했는지를 정리한 표는 자료에 없다.

챗봇 모델 선택 — retrieval인가 generative인가

두 방식을 비교한 뒤 왜 앞의 것을 골랐는지가 슬라이드에 적혀 있다.

슬라이드 31은 챗봇의 두 가지 모델을 비교한다. 원문 그대로 옮기면 이렇다.

모델동작적어 둔 한계
Retrieval-based model사용자의 질의에 미리 만들어 둔 형태로 대답 — “{친구}와 {싸웠던 일} 때문에 {슬픈} 감정이 들었구나.”모든 주제와 경우에 대해 모든 답을 만들 수 없음
Generative model사용자의 질의에 챗봇이 자동으로 답변을 생성챗봇이 항상 정확한 대답을 할지 보장할 수 없음

그리고 선택의 기준을 이렇게 적었다. “특정 주제에 대해서는 Retrieval-based model, 열린 주제에 대해서는 generative model로 문장 생성”. 결론은 다음 한 줄이다.

“상담 위로는 너무 다양한 원인과 감정이 있지만 비슷한 위로를 할 수 있어 retrieval-based model 사용”

이 판단은 앞 단계의 설계와 앞뒤가 맞는다. 원인은 39갈래로 갈라지지만 그 각각에 필요한 위로는 결국 몇 가지 지지 표현으로 수렴한다고 본 것이고, 그렇다면 문장을 생성할 필요 없이 범주를 알아맞히기만 하면 된다. 잘못된 위로가 상처가 될 수 있는 도메인에서 생성 모델을 피한 것도 보수적인 선택으로서 타당하다. 슬라이드에 적힌 예시가 원인 명사·행동 동사·감정 형용사 세 슬롯을 채우는 형태여서, 앞의 품사 세 가지 추출과도 정확히 대응한다.

다만 이 슬롯 채우기 방식은 실제 앱에 구현되지 않았다.

구현 — 앱 안에 실제로 들어간 것

앱이 답변을 고르는 방식이 소스에 그대로 남아 있다.

수업이 다룬 범위는 텍스트마이닝과 사회연결망분석이었고, 안드로이드 개발은 거기에 없었다. 그런데도 챗봇 앱을 만들어 냈고, 슬라이드에는 “안드로이드 스튜디오로 제작 성공”이라고 적혀 있다. 실기기에 설치까지 마친 캡처가 2019년 12월 9일 자로 남아 있다.

중요한 것은 앱이 답변을 고르는 방식이다.

  • 입력에 ‘우울’, ‘자살’, ‘정신병’, ‘정신병원’, ‘우울증’ 중 하나가 있으면 → 우울 관련 위로 문장
  • 다음 분기(가정폭력) → “가족의 울타리가 제 역할을 하지 못해서…”로 시작하는 문장
  • 입력에 ‘혼자’, ‘좋다’, ‘미안하다’, ‘기다리다’ 중 하나가 있으면 → 이별 관련 위로. 이별 극복 방법을 실험한 내용까지 담은 긴 답변이다.
  • 어느 것에도 걸리지 않으면 → “애구.. 많이 힘들었을 것 같아요. 최근에 더 무슨 일이 있었나요?”

앱 안에 들어간 것은 TF-IDF 파이프라인이 아니라 입력에 특정 키워드가 들어 있는지 검사하는 세 갈래 분기와 기본 응답 하나다. 39개 범주로 나누고 범주마다 핵심어를 뽑은 작업의 결과가 앱에서는 세 갈래로 줄었다.

키워드 선정도 분석 결과와 이어져 있지 않다. 이별 분기의 ‘혼자’·‘좋다’는 어느 고민에나 나올 수 있는 말이라, 이별과 무관한 문장이 이별 위로로 흘러갈 가능성이 높다. 앞에서 뽑은 범주별 상위 단어(가정폭력의 ‘때리다·맞다·폭력’ 같은)를 그대로 조건으로 옮겨 넣었다면 훨씬 나았을 텐데, 그렇게 되어 있지 않다. 분석과 구현이 같은 사람의 손에서 나왔는데도 서로 연결되지 않은 셈이다.

챗봇 화면이 답변을 고르는 부분 — 입력에 특정 키워드가 들어 있는지 검사하는 세 갈래 분기와, 어느 것에도 걸리지 않을 때의 기본 응답이 보인다.
챗봇 화면이 답변을 고르는 부분 — 입력에 특정 키워드가 들어 있는지 검사하는 세 갈래 분기와, 어느 것에도 걸리지 않을 때의 기본 응답이 보인다.

완성된 화면 — 기본 응답이 대화를 이끈다

화면에 실린 대화 예시가 어느 분기에서 나왔는지 대응이 된다.

완성 화면의 상단 카피는 프로젝트의 정의를 그대로 옮긴 것이다. “정서적으로 내 편이 필요한 사람들에게 / 공감과 따뜻한 위로 한마디”. 화면에 실린 대화는 다음 순서로 진행된다.

화자발화어디서 나온 문장인가
포근이“무슨 힘든일이 있었나요? 포근이에게 말해도 돼요.”대화 시작 인사
사용자“너무 힘들어...”짧은 입력 — 어느 키워드에도 걸리지 않음
포근이“애구.. 어떤 일들이 힘들게 만들었을까요... 최근에 무슨 일이 있었나요?”어느 키워드에도 걸리지 않았을 때의 기본 응답
사용자“일도 안 풀리고 인간관계 속에 너무 지쳐.. 우울해..”‘우울’이 들어가 첫 번째 분기로 감

이 흐름을 앞 절의 분기와 맞춰 보면 한 가지가 분명해진다. 챗봇이 되묻는 두 번째 발화는 고민을 더 끌어내기 위해 설계한 질문이 아니라 아무 키워드에도 걸리지 않았을 때 나오는 기본 응답이다. 다만 화면의 문장과 캡처에 남은 기본 응답 문장(“애구.. 많이 힘들었을 것 같아요. 최근에 더 무슨 일이 있었나요?”)은 표현이 조금 다르다. 두 캡처의 시점이 다른 것으로 보이고, 자료로 확인되는 것은 역할이 같다는 데까지다. 결과적으로는 잘 작동한다. 짧은 입력에서는 원인 명사도 감정 형용사도 뽑히지 않으니 되묻는 것이 맞고, 그 되물음에 사용자가 ‘일·인간관계’(원인)와 ‘지쳐·우울해’(감정)를 담은 문장을 내놓으면 그때 분기가 걸린다.

실패했을 때의 응답이 대화를 진행시키는 장치가 되었다. 의도한 설계는 아니지만, 크롤링 단계에서 ‘텍스트 양이 불충분한 글’을 문제로 본 것과 같은 구조가 대화에도 나타난 셈이다.

완성된 '포근이' 대화 화면 — 아래에서 위로 읽는다. 짧은 입력("너무 힘들어...")에는 되묻는 기본 응답이 나가고, 원인과 감정이 함께 담긴 문장이 오면 그때 위로 분기로 넘어간다.
완성된 '포근이' 대화 화면 — 아래에서 위로 읽는다. 짧은 입력("너무 힘들어...")에는 되묻는 기본 응답이 나가고, 원인과 감정이 함께 담긴 문장이 오면 그때 위로 분기로 넘어간다.

남은 것 — 분석과 앱 사이의 간격

잘한 것과, 자료를 다시 읽어야 보이는 것.

이 프로젝트에서 실제로 잘한 것은 결정마다 근거를 붙인 습관이다. 수집원은 이용실태조사 표로, 위로 문구는 Bolger의 논문과 『당신이 옳다』로, 챗봇 방식은 retrieval과 generative의 비교로 골랐다. 학부 개인 과제에서 이 세 가지를 다 붙여 놓은 것은 지금 다시 봐도 나쁘지 않다.

그러나 자료를 처음부터 다시 읽고 나면 더 크게 보이는 것은 분석과 구현 사이에 남은 간격이다.

  • 39개 범주를 만들고 범주마다 핵심어를 뽑았지만, 앱은 그중 세 갈래만 구분하고 그 키워드도 분석 결과에서 가져오지 않았다.
  • ‘불용어 사전을 직접 제작’했다고 썼지만 실물은 17개짜리 목록이었고, 그 결과가 상위 단어 목록에 ‘가다·보다·없다’로 남았다.
  • t-SNE를 돌렸지만 군집은 나오지 않았고, 뒤 단계에서 실제로 쓴 것은 단어 목록이었다.
  • 분류는 사람이 손으로 붙인 라벨이므로 정확도라고 부를 수치가 처음부터 없다.
  • 범주 목록은 39개라고 적혔지만 실제 나열은 38개이고, 결과 슬라이드에는 목록에 없는 ‘조부 갈등’이 등장한다.

학습 자료 쪽에도 흔적이 남아 있다. 같은 폴더의 학습 자료 한 편은 통째로 꼬꼬마(KKMA) 형태소 분석기의 알고리즘 설명, 품사 태그표, 결합 조건표다. 형태소 분석기를 고르기 전에 문서를 꽤 깊이 읽었다는 뜻인데, 정작 분석에 쓴 것은 다른 분석기였다. 또 수업 노트의 절반은 연결 중심성·매개 중심성·구조적 공백 같은 사회연결망분석 이론인데, 이 프로젝트에는 네트워크 분석이 전혀 들어가지 않았다. 수업 이름의 앞 절반은 쓰이지 않은 셈이다.

여기서 남은 결론은 두 가지다. 하나, 텍스트 분석의 결과는 모델이 아니라 직접 설계한 기준—불용어 목록과 범주 체계—이 결정한다. 둘, 분석이 앱이 되는 지점에서 대부분이 새어 나간다. 파이프라인을 만드는 것과 그 파이프라인이 실제로 사용자의 입력에 붙어 도는 것은 다른 일이고, 이 프로젝트는 앞의 것만 했다.

한 번 만든 파이프라인을 다른 서비스에 그대로 썼다

포근이를 위해 만든 다섯 단계를, 사용자군이 완전히 다른 앱(테럿)에 그대로 옮겼다.

공통 파이프라인 — 한 번 만들어 두 서비스에 썼다 ① 수집 웹 크롤링 네이트판 · 정신의학신문 / 아동 질문 ② 전처리 품사 태깅 · 어간 추출 직접 만든 불용어 사전 ③ 분류 · 핵심어 카테고리 분류 + TF-IDF 원인 명사 · 감정 형용사 · 행동 동사 ④ 응답 선택 카테고리별 응답 사전 문헌 근거로 고른 위로 / 영어 문장 ⑤ 앱 Android Studio 기획 · 구현 모두 1인 ‘포근이’ — 위로 챗봇 사회데이터 분석과 텍스트 마이닝 · 개인 고민 글을 39개 카테고리로 분류하고, 문헌에서 고른 위로를 건넨다 ‘테럿’ — 영어 앵무새 (출시) 테크노 경영학 · 완전히 다른 사용자군 아이의 한국어 문장을 영어로 되풀이하고, 한국어 질문에 영어로 답한다
같은 파이프라인, 다른 사용자군. 고민 상담 글에 맞춰 만든 수집 → 전처리 → 분류 → 응답 선택 → 앱의 다섯 단계를, 유아 영어 앱에 그대로 옮겨 재사용했다. 바뀐 것은 크롤링 대상과 응답 사전뿐이었다.

여기서 배운 것

  1. 텍스트 분석의 결과를 좌우한 것은 모델이 아니라 직접 설계한 기준이었다. 불용어 목록이 17개에서 멈추자 범주별 상위 단어에 ‘가다·보다·없다’가 그대로 남았고, 범주 체계를 문서 없이 늘려 가자 겹치는 범주와 개수 불일치가 남았다.
  2. 결정마다 근거를 붙이는 습관은 이 프로젝트에서 자리를 잡았다. 수집원은 이용실태조사 표로, 위로 문구는 Bolger(2000)의 invisible support 논문과 『당신이 옳다』로, 챗봇 방식은 retrieval과 generative의 비교로 골랐다.
  3. 만든 파이프라인이 실제 제품에 들어가지 않으면 아무것도 전달되지 않는다. 39개 범주와 범주별 핵심어를 뽑고도 앱에는 세 갈래 키워드 분기만 들어갔고, 그 키워드조차 분석 결과에서 가져오지 않았다.
  4. 안 되는 것을 확인한 것도 결과다. t-SNE에서 군집이 나오지 않는 것을 그림으로 본 뒤 자동 분류 대신 사람이 라벨을 붙이는 쪽으로 갔고, 그 판단은 결과적으로 맞았다.
  5. 실패 경로가 제품의 기본 동작이 된다. 어느 키워드에도 걸리지 않을 때 나가는 기본 응답이 실제로는 대화를 이어 주는 가장 중요한 발화였다.