융합캡스톤 회고 1

MINJU·2022년 12월 27일
0
post-thumbnail

끝이 보이지 않았던 융합SW 연계전공 졸업프로젝트가 끝났다 🎈

사실 끝난진 좀 됐지만, 남은 할 일을 바삐 처리하고 이제서야 글을 쓰게 되었다.
한 학기동안의 긴 여정이 글에 잘 담길 수 있을까 약간 걱정이지만 일단 시작 ~


📖 프로젝트 소개

프로젝트 링크는 여기!

프로젝트 목표

우리의 프로젝트 목표는 아래와 같았다.

프로젝트 배경

그리고 이걸 왜 만들어야하냐면

이렇기 때문이다😶
(PPT 열심히 만든 만큼 이럴 때 써야하지 않나 싶다)



해당 주제를 결정하게 된 주요한 요인은 바로 딥러닝에 대한 심적인 기피였다.💦

프로젝트를 시작하기 전, 학교에서 제공해주는 예비 주제들을 받아볼 수 있었는데 하나같이 모두 ! 머신러닝이 접목된 프로젝트들이었다. 💧

딥러닝에 대한 깊은 지식이 없는 상태로 시작한 프로젝트였고,
해당 프로젝트에서 큰 규모의 백엔드를 담당하고 싶었던 나는(우리는) 딥러닝에서 최대한 힘을 빼고자 노력했다.

따라서 대시보드 웹을 주제로 잡게 된 것이다.




여기서 우리가 간과한 부분이 등장한다.

바로 "모니터링 웹"에서의 모니터링에 대한 정의이다. 모니터링이라는 개념이 범위가 매우 넓다보니, 회의를 진행할 때마다 미팅을 진행할 때마다 기능이 수도 없이 추가됐었다.

이렇게 추가만하다가는 아무것도 못하고 끝날 수도 있겠다.. 는 생각에 우선 모니터링 상황을 정의하기로 결정했다.

이렇게 카테고리를 명확히 정해놔야, 우리끼리도 헷갈리지 않을 수 있을 것 같았고 교수님들의 다양한 조언들 속에서도 우리의 길을 유지해나갈 수 있을 것이라 생각했다.



모니터링 상황

그 결과 정의된 상황은 총 다섯가지이다.

위급 상황의 경우 빠른 상황 전달이 중요한 기능이므로, 관리자 대시보드 web에 실시간 알림 팝업을 띄우고자 결정하였고

원활한 일상 생활 확인의 경우 모니터링 신뢰도 확보가 중요하므로, IoT 정보와 챗봇 응답 정보를 크로스 체킹하여 진위 여부를 판단하도록 결정하였다.

각 상황에 대한 플로우차트는 github에서 확인할 수 있다!



💻 개발

위와 같은 프로젝트 설계의 결과로서 우리는

  1. IoT (NodeMCU)
  2. 챗봇 (KoGPT, KoELECTRA, KoBERT) + Flask 서버
  3. 안드로이드
  4. Spring Boot 서버
  5. React

를 모두 사용하는.. 프로젝트를 만들게 되었고 😶


시스템 구조도

그 결과 등장하게 된 시스템 구조도는 아래와 같다.

다같이 힘들었지만 ㅠ ㅠ 그래도 진짜 볼 때마다 뿌듯하다.


내가 담당한 부분

나는 이 프로젝트에서 챗봇챗봇 서버, 그리고 Spring Boot 서버 일부를 담당했다!

기능이 많고 복잡한 만큼, 차근차근 하나하나씩 설명해보고자 한다.


(1) 챗봇 학습

우선 처음에 가장 골머리를 앓았던 챗봇 학습 파트이다.

챗봇을 학습할 때 가장 많이 참조했던 레포지토리는

  1. 아띠어리

  2. 물어봐

이 두개의 repository였다.

연구실 과제 덕분에, 챗봇의 개념과 챗봇을 학습시킨다는 의미.. 와 같은 겉핥기 지식은 다 알고 있었지만 정작 학습시키는 방법은 전혀 몰랐던 내게 아주 큰 가이드가 되어준 분들이시다😥



레퍼런스가 되는 블로그를 찾았으므로 이를 기반으로 학습을 진행했다.

코랩 기본 버전으로도 충분히 가능했을 것 같지만 (열정에 불타던 나는) 코랩 pro를 결제했고

코랩에서 각 레퍼런스별로 학습을 시킨 후 해당 pt 모델을 내 local로 가져와서 pycharm에서 환경을 설정했다.

용량 부족 이슈로 지금은 삭제했지만ㅠㅠ checkpoint라는 폴더에 pt 파일을 넣어놓았고


서버 로직에서

  1. 사용자 발화가 들어오면 KoBERT를 활용해서 감정의 긍정, 부정을 확인
  2. 긍정 감정일 경우 대화 생성 모델인 KoGPT 모델로 답변 생성
  3. 부정 감정일 경우 심리 상담 데이터셋 기반으로 답변을 찾아내는 KoELECTRA모델로 답변 생성
  4. KoBERT를 통해 분류해낸 사용자 발화의 긍부정을 서버에 저장 (한 대화 내 가장 많이 등장한 감정을 서버에 저장 )

하는 방식으로 사용했다.



참조 레퍼런스에서는 KoBERT를 활용하여 총 7가지의 감정으로 사용자 발화를 분류해내고 있는데,
우리의 모델은 성능이 그리 좋지 않았기도 했고 단순히 긍부정으로 분류하는 것이 더 정확할 것 같아서 그렇게 진행했다


KoELECTRA의 경우에는 대부분 부정적인 대화가 들어있는 심리상담데이터셋을 기반으로 학습했기 때문에 "나 오늘 기분 좋아"와 같은 긍정적인, 그리고 일상적인 발화에 답변을 제대로 뱉어내지 못헀다.

따라서 긍정 발화의 경우 KoGPT로, 부정 발화의 경우에는 KoELECTRA로 답변을 생성하도록 로직을 작성했다.


시연 영상 캡쳐를 간단히 첨부해보자면

이렇게 답변을 뱉을 수 있고
(대화를 종료하는 시점을 정해야했었는데, 그냥 사용자가 "종료"라고 말하는 시점을 종료 시점으로 정했다. 따라서 모든 답변의 뒤에 "대화를 더 진행하시려면 계속 말씀해주시고, 아니면 종료를 말씀해주세요" 라는 말이 붙는다 🙄)

이렇게 대시보드에 대화가 띄워진다.

관리자는

이렇게 관리대상 노인별로! 대화를 확인할 수 있고
사용자가 아직 종료라고 말하기 전이면 대화 진행중이라고 문구가 뜨는데

종료라고 말한 이후이면 해당 대화 턴에서의 최빈 감정값이 대화 보기 옆에 노출된다.
(ex. 한 대화 내에서 사용자 발화가 긍정, 부정, 부정, 부정인 경우 해당 대화가 끝나면 해당 대화의 "대화보기" 버튼 옆에 감정:부정이라고 뜬다!)



챗봇 학습 방법은 위 블로그에서도 잘 나와있기도하고,
개인적으로 환경설정과 같은 부분은 삽질의 과정이 필수적이라고 생각하기 때문에...

뭣보다 내가 챗봇 학습에 대해 엄청나게 아는게 많은 것은 아니므로 이만큼 작성하는데서 끝내고자한다 😂

챗봇 학습을 진행하면서 많이 배웠던 부분은

  1. 내 니즈에 맞는 오픈소스를 찾아서 직접 적용해보는 경험
  2. 인터넷의 한정된 정보를 기반으로 그 내용을 이해하고 구현해보는 경험
  3. 돌아가는 챗봇을 만들었다는 점에서 ...의 뿌듯함

정도가 될 것 같다
으하하


그래도 챗봇 input으로 뭐가 들어가야하고, pt 파일은 무엇이고, intent가 뭐고 entity가 뭔가에 대한 개념이 연구실 과제를 통해 잡혀있었기 때문에 가능했던 일이 아닌가싶다.

학습할 땐 정말 정말 막막했는데 그래도 어찌저찌 해내서 다행이라는 생각이 드는 파트였다.



(2) Flask 서버 구축

1. RDS

SQLite는 여러번 써봤는데,
쌩으로 쿼리 짜는 것보다 더 쉬운 방법은 무엇일까..하다가 ORM을 도입하게 됐다

요렇게 ORM 구조를 잡고

config.py를 작성했다.

Local DB 활용하여 개발하다가
마지막에 RDS 연동헀다!

위와 같은 코드로 작성하니까 DB 변경할 때도 코드 몇 개만 바꾸면 돼서 꽤나 유용했다



2. controller

챗봇이 해야하는 일이

  1. 화장실 모니터링
  2. 식사 모니터링
  3. 일상 안부 대화

이므로 위와 같이 controller를 분리했다. (dbController는 ORM을 위해 사용되는 코드이다.)


각 컨트롤러는 아래의 시나리오대로 로직을 설계했다.



1. 일상안부

일상 안부는 위에서 말한 로직을 따라간다.

@emotion.post("/start")
def start_emotion():
    json = request.get_json()
    phone_number = json["phone_number"]
    stay = dbService.is_client_at_home(phone_number)
    client_id = dbService.get_client_id(phone_number)
    if not stay:
        return return_answer(NOT_IN_THE_HOUSE, DT_END, -1)
    question = responseService.get_call_to_say_hi()
    conv_id = dbService.create_conversation(question, client_id)
    dbService.change_response_status(phone_number, False)
    return return_answer(question, DT_EMOTION, conv_id)  # 응답 시간 기록하기 -> 정상 비정상 여부
    # -> 배치로 n분 동안 대답 x인 사람 움직임 X인 사람 알려주기

사용자가 집 안에 있으면(if stay) 정해진 일상 안부 리스트에서 랜덤으로 질문을 출력하고(responseService.get_call_to_say_hi())


@emotion.post("/chat")
def chat_emotion():
    json = request.get_json()
    input = json["input"]
    input_split = input.split(" ")
    phone_number = json["phone_number"]
    conv_id = json["conv_id"]
    quit_answer = responseService.is_quit_answer(input_split)
    dbService.change_response_status(phone_number, True)
    print(quit_answer)
    if quit_answer:
        dbService.update_conversation_emotion(conv_id)
        return return_answer(QUIT, DT_END, conv_id)  # 종료 -> 감정값 전달
    phone_number = json["phone_number"]
    # 긍정 부정 판단
    classification = emotion_classification.predict(input)
    print(classification)
    is_negative = responseService.is_negative_answer(classification)
    if is_negative:
        emotion = "부정"
        answer = koelectra_qa.get_answer(input) 
    else:
        emotion = "긍정"
        answer = kogptchat.predict(input)
    dbService.update_conversation(conv_id, emotion, (input + "," + answer), "")
    return return_answer(answer + IS_QUIT, DT_EMOTION, conv_id) # 종료 -> 응답 했다고 갱신

발화의 긍부정을 판별하여 그에 맞는 모델에서 답변을 발화하도록 구현하였다

그리고 종료를 인식하면 최빈 감정값을 저장한다.




Flask 서버는 로직 제어에 dialog_type이라는 JSON 필드를 사용한다!

클라이언트에게 해당 dialog_type을 넘기고 그에 맞게 제어하도록 구현한 것이다.



2. 화장실 감지

화장실 감지도 동일하다!


특정 시간에androidalarm manager를 통해 화장실 감지 요청 로직이 들어오면,


서버는 사용자의 외출 여부를 파악하고,

def get_today_bathroom_count(phone_number):
    client = get_client(phone_number)
    return len(db.session.query(LastMovedTime).filter_by(client_id=client.id).filter(
        func.date(LastMovedTime.last_moved_time) == date.today()
    ).filter(LastMovedTime.location == "bathroom").all())

IoT 정보가 기록된 DB를 활용하여 사용자의 화장실 이용 횟수를 감지 한 후
그에 맞는 질문을 전달한다.


로직 흐름을 제어하기 쉽도록 Yes or No로 대답할 수 있는 질문들로 구성하였다.

그리고 변비가 있다고 할 때, 아프다고 할 때 그에 맞게 사용자의 특이사항을 대화 db에 같이 저장하도록 구현하였다.

시연 영상 캡쳐는 아래와 같다.



3. 식사감지

화장실 모니터링 로직과 비슷하다!


특정 시간에 android의 alarm manager를 통해 식사 모니터링 요청 로직이 들어오면,

서버는 사용자의 외출 여부를 파악하고,
식사 여부 질문을 한다.

그리고 그로 들어온 답변의 Yes or No를 구분한 후
필요에 따라 IoT에 기록된 주방 및 출입 여부 기록을 확인하고

그에 맞는 시나리오를 이어나간다.

마찬가지로 특이 사항이 있으면 대화 DB에 저장하도록 구현하였다.

아래는 시연 영상 캡쳐이다.

식사를 했다고 대답했는데, 2시간 이내의 주방 움직임 감지 기록이 없는 경우
그에 맞는 답변을 출력하고, 대시보드에도 특이사항을 노출하고 있음을 확인할 수 있다!!

대화를 통해 파악할 수 있는 특이사항은 응급상황이라고는 생각하지 않아서, 저렇게 관리자가 사용자의 대화 기록을 볼 때 편하게 특이사항과 최빈 감정값을 파악할 수 있도록 도와주는 정도로만 구현했다!!



챗봇이랑 Flask 서버만하더라도 내용이 정말 많다 ~ ~

회고는 2편에서 이어진다아❄

0개의 댓글