구글 머신러닝 스터디 잼 후기

우리 머신러닝 공부 해보지 않을래?

소프트웨어 엔지니어로 일하고 있는 여자친구로부터 아주 멋진 제안 하나가 들어왔다. 머신러닝 공부를 같이 해보자는 것이었다. 이는 나에게 아주 솔깃한 제안이었다. 마침 현재 하고 있는 연구분야에 적용할 방법 중 하나로 머신러닝 및 딥러닝을 고려하고 있던 터였다. 그러면서 머신러닝 공부를 할 수 있는 프로그램을 소개받았다. 구글에서 진행하는 ‘스터디잼’ 이었다. 이것이 어떤 프로그램인지 찾아봤다. 이번에 처음 시도되는 것은 아니고 이미 2018년 4월부터 시작해서 12월까지 8개월이라는 과정동안 총 5단계로 구성된 구글 클라우드 스터디잼1이 있었다. 2019년에는 클라우드 스터디잼 과 함께 머신러닝 스터디잼이라는 주제로 두 종류의 스터디잼 프로그램이 마련되었다. ‘알파고 이후로 딥러닝에 대한 사람들의 높아진 관심과 기대에 부응이라도 하듯 구글이 참 좋은 교육 프로그램을 무료로 이렇게 운영해주는 구나.’ 라는 생각이 들었다.

스터디잼은 한 사람이 그룹장이 되어 같이 공부할 그룹원들을 모집하여 지원하는 형태로 접수되었다. 그룹원들을 구성하여 그룹장들의 참가 신청이 2월 8일을 마감으로 하여 끝났다. 우리 그룹도 제일 처음에 머신러닝 공부를 해보자고 제안한 여자친구가 그룹장이 되었고, 그룹장으로 포함해 총 5명의 소규모 인원으로 조직되었다. 그룹원들은 IT 기업에서 개발자로, 아이들에게 코딩을 가르치는 교사로, 그리고 나와 같은 학생으로 다양한 곳에서 근무했지만 머신러닝을 공부하겠다는 공통된 일념 하나로 스터디 그룹이 빠르게 만들어졌다.

그림

스터디 잼 일정

잠깐만, 내가 기대했던 것이 아니잖아?

그룹장들의 참가신청이 끝나고 그룹이 모두 구성된 후, 2월 9일에 구글에서 최종 참석자 대상으로 스터디잼 안내 메일을 받았다. 구글에서는 그룹장들에게만 메일을 보낸다고 되어 있었는데, 그룹장의 메일주소가 뭔가 잘못되어 있을 경우에는 나머지 그룹원들에게 메일을 보낸다고 되어 있었다. 우리 그룹의 그룹장 메일주소로 안내 메일이 가는 데 뭔가 문제가 있어서 나를 포함한 4명의 그룹원들이 직접 안내 메일을 받았다.

안내 메일은 스터디의 시작부터 종료 후 수료까지의 중요 가이드라인을 담고 있었다. 먼저 구글 스터디잼에서 제공하는 강좌 플랫폼인 퀵랩을 무료로 사용할 수 있는 바우처를 제공받아 아래의 4개 강좌를 듣고 실습을 2월 24일까지 완료해야 하는 미션이었다.

어? 근데 내가 예상했던 교육 프로그램의 내용과는 조금 달랐다. 나는 머신러닝 및 딥러닝에 대한 이론적이고 기초적인 개념들을 공부할 수 있는 동영상 강의를 기대했었다. 하지만 퀵랩에서 제공하는 강의 형태인 ‘랩’은 구글 클라우드 플랫폼을 활용하여 직접 해당 내용들을 곧바로 실습해보는 구성으로 되어 있었다. 여기서 약간의 실망을 했다. 하지만, 보험도 가입할 때 약관을 자세히 잘 읽어봐야 하듯 스터디잼을 소개하는 페이지에서 아랫 부분을 잘 읽어봤어야 했다.

이론이 아닌 실습 위주의 교육자료로 구성되어 있어 현업에서 머신러닝이 어떻게 적용되는지를 구글 클라우드 환경에서 머신러닝 API를 사용하여 공부합니다: Google Cloud Speech API, Cloud Natural Language API

그림

이제서야 스터디잼의 취지가 이해가 갔다. 구글에서 만든 구글 클라우드 플랫폼(GCP)을 쓰는 방법을 위주로 한 교육 프로그램이었던 것이다. 거기에 머신러닝이라는 조미료를 조금 더 보태서 말이다. 나 말고도 나머지 그룹원들도 ‘머신러닝’ 이라는 키워드에 이끌려 스터디잼을 시작하게 된 경우가 많았다. 하지만 이 모든 것이 제대로 된 설명을 읽지 않았던 책임이 크니 자기 자신을 탓할 수밖에 없었다. 따흐흑…

무료로 GCP 를 사용할 수 있으니, 배워보자!

스터디잼에서 제공하는 퀵랩 바우처를 통해 실습에 사용되는 구글 클라우드 플랫폼을 일정 기간동안 무료로 사용할 수 있다. 아마존이나 마이크로소프트의 클라우드 서비스들도 제대로 사용해 본 경험이 없지만 이 참에 한번 구글을 통해 기회를 얻었으니 클라우드 서비스를 사용해보는 경험을 해 보자고 마음 먹었다.

그림

퀵랩의 한 강좌에 들어가면 위 화면처럼 나타난다. ‘랩 시작’ 이라는 초록색 버튼을 누르면 15분 타이머가 작동되고 실습을 위한 임시 계정으로 구글 클라우드 플랫폼이 열리고, 강좌에서 안내하는 지시 사항에 따라 실습을 마쳐야만 한다. 제한시간 내에 마쳐야 하기 때문에 ‘충분한 시간을 갖지 못하고 실습을 쫒기듯이 해야 하는 거 아닌가?’라는 생각이 들면서, 도대체 왜 제한시간을 걸어놨을까 의아했다. 뭔가 공부를 할 때 제한시간 있는 것을 매우 싫어하는 나로서는 ‘아, 이거 힘든 스터디가 되겠구나’라는 걱정까지 들었다. 하지만 퀵랩을 시작하기 전에 미리 실습 내용을 한번 훑어볼 수 있고, 열린 구글 클라우드 플랫폼으로 실습을 진행하는 데에는 5분이 채 걸리지 않았다. 내가 괜한 걱정을 했던 모양이다. 제한시간이 있는 이유를 잘 생각해 보니 아무래도 실습을 위해 임시로 생성한 구글 클라우드 플랫폼을 계속 열어둘 수 없으니 제한된 시간만 제공하려는 목적이 아닐까 추측해 보았다.

실습을 위해서라면 충분했지만 구글 클라우드 플랫폼을 조금 더 살펴보려는 목적에는 이렇게 몇 분만 열렸다 닫히는 임시 계정은 조금 부족함이 있었다. 구글 클라우드 플랫폼에서 설정했던 것들은 계정 만료와 함께 다 사라지고, 다른 기능들을 더 살펴보고 싶지만 그러지 못했다. 하지만 꼭 스터디잼을 하지 않더라도 현재 구글에서 300 달러의 무료 평가판 크레딧을 제공하고 있다는 안내를 발견했고, 바로 이용해 보면서 실습 내용도 한번 더 진행해 보면서 복습을 할 수 있었다. 다음으로는 공부했던 내용들을 본격적으로 살펴보려고 한다.

음성 인식을 위한 구글 클라우드 Speech API

스터디잼에서 진행하는 주제는 크게 2가지 이다. 하나는 Speech API 를 이용하여 음성인식 실습을 해 보는 것이고, 다른 하나는 Natural Language API 를 이용하여 자연어 처리 실습을 진행해 보는 것이다. 먼저 음성인식 실습에 대해 알아보자. 구글 클라우드 플랫폼에서 API 를 사용하기 위한 API key 생성을 진행한 다음 아래와 같이 음성인식에 파일 request.json 를 작성하였다.

{
  "config": {
      "encoding":"FLAC",
      "sample_rate": 16000,
      "language_code": "en-US"
  },
  "audio": {
      "uri":"gs://cloud-samples-tests/speech/brooklyn.flac"
  }
}

음성인식에 넣어줄 오디오 파일의 형식과 저장 위치들이 담긴 파일이다. 위의 오디오 파일을 재생해 보면 남성의 목소리로 ‘How old is the Brooklyn Bridge? (브루클린 교2는 지어진지 얼마나 오래 됐나요?)’ 라는 질문 하나가 녹음되어 있는 것을 알 수 있다. 입력 파일을 지정해 주었으니 이제 음성인식이 제대로 되는지 살펴볼 차례다.

curl -s -X POST -H "Content-Type: application/json" --data-binary @request.json \
  "https://speech.googleapis.com/v1beta1/speech:syncrecognize?key=${API_KEY}"

위와 같은 명령어로 request.json 파일과 Speech API 의 주소를 지정하면 다음과 같은 결과물을 얻을 수 있다.

{
  "results": [
    {
      "alternatives": [
        {
          "transcript": "how old is the Brooklyn Bridge",
          "confidence": 0.98267895
        }
      ]
    }
  ]
}

오디오 파일로부터 정확히 다 알아들었음을 확인할 수 있었다! transcript 항목에 음성인식이 된 결과물을 확인할 수 있고, confidence 항목으로부터 이러한 결과물이 얼마나 정확도가 높은지를 확인할 수 있다.

구글 Speech API 는 다른 언어로도 음성인식을 진행할 수 있다고 한다. 이번엔 프랑스어로 진행을 해 보자. request.json 파일의 내용을 다음과 같이 바꾸고 다시 음성인식을 수행해 보았다. (음성은 여기에서 들을 수 있다.)

{
  "config": {
      "encoding":"FLAC",
      "languageCode": "fr"
  },
  "audio": {
      "uri":"gs://speech-language-samples/fr-sample.flac"
  }
}

결과는 다음과 같았다. 약 0.97 의 정확도로 음성 인식이 수행된 것을 볼 수 있다.

{
  "results": [
    {
      "alternatives": [
        {
          "transcript": "maître corbeau sur un arbre perché tenait en son bec un fromage",
          "confidence": 0.9710122
        }
      ]
    }
  ]
}

Natural Language API 를 이용한 자연어 처리

음성인식을 통해 오디오를 글자로 변환했다면, 이제는 글이나 문장을 이루고 있는 단위 요소(entity, 개체) 의미를 분석하는 자연어 처리의 한 분야인 개체명 인식(entity analysis)를 시도해볼 수 있다. 아래와 같은 문장을 구글 클라우드 Natural Language API 를 통해 넣어주면 entity 단위로 종류와 의미를 알아낼 수 있다.

Michelangelo Caravaggio, Italian painter, is known for ‘The Calling of Saint Matthew’. (이탈리아의 화가 미켈란젤로 카라바조는 ‘성 마태오의 소명’이라는 작품으로 유명하다.)

다음과 같이 gcloud 명령어를 통해 분석을 수행할 수 있다. 입력할 텍스트는 --content 옵션을 통해 넣어주면 된다.

gcloud ml language analyze-entities --content="Michelangelo Caravaggio, Italian painter, is known for 'The Calling of Saint Matthew'."

명령어를 통해 분석한 결과는 다음과 같다. 대상이 되는 entity 가 무엇인지는 name 항목을 통해 알 수 있고, 해당 entity 가 어떤 종류인지를 type 을 통해 알 수 있다. 또한 위키피디아와 같은 백과사전에서 그 정보를 찾을 수 있는 경우에는 훌륭하게도 metadata 를 통해 정보를 얻을 수 있는 링크도 제공하고 있다. ‘미켈란젤로 카라바조’라는 entity 는 ‘사람’이라는 종류로, ‘이탈리아의’라는 entity 는 ‘지명’이라는 종류로 제대로 개체명 인식 분석이 되었음을 확인할 수 있다. 그리고 0 과 1 사이의 값을 갖는 salience 항목3은 해당 entity 가 문장에서 얼마나 집중도가 높은지를 나타낸다. 여기서는 미켈란젤로 카라바조라는 인물에 대하여 이야기하고 있으므로 ‘미켈란젤로 카라바조’라는 entity 의 salience 값이 0.83 으로 매우 높음을 알 수 있다.

{
  "entities": [
    {
      "name": "Michelangelo Caravaggio",
      "type": "PERSON",
      "metadata": {
        "wikipedia_url": "http://en.wikipedia.org/wiki/Caravaggio",
        "mid": "/m/020bg"
      },
      "salience": 0.83047235,
      "mentions": [
        {
          "text": {
            "content": "Michelangelo Caravaggio",
            "beginOffset": 0
          },
          "type": "PROPER"
        },
        {
          "text": {
            "content": "painter",
            "beginOffset": 33
          },
          "type": "COMMON"
        }
      ]
    },
    {
      "name": "Italian",
      "type": "LOCATION",
      "metadata": {
        "mid": "/m/03rjj",
        "wikipedia_url": "http://en.wikipedia.org/wiki/Italy"
      },
      "salience": 0.13870546,
      "mentions": [
        {
          "text": {
            "content": "Italian",
            "beginOffset": 25
          },
          "type": "PROPER"
        }
      ]
    },
    {
      "name": "The Calling of Saint Matthew",
      "type": "EVENT",
      "metadata": {
        "mid": "/m/085_p7",
        "wikipedia_url": "http://en.wikipedia.org/wiki/The_Calling_of_St_Matthew_(Caravaggio)"
      },
      "salience": 0.030822212,
      "mentions": [
        {
          "text": {
            "content": "The Calling of Saint Matthew",
            "beginOffset": 69
          },
          "type": "PROPER"
        }
      ]
    }
  ],
  "language": "en"
}

또 다른 예제 문장을 가지고 한번 더 시도해 보았다.

Joanne Rowling, who writes under the pen names J. K. Rowling and Robert Galbraith, is a British novelist and screenwriter who wrote the Harry Potter fantasy series. (J. K. Rowling 과 Robert Galbraith 라는 필명으로 글을 쓴 조앤 롤링은 영국의 소설가이며, 해리포터 판타지 시리즈를 쓴 시나리오 작가이다.)

다음과 같은 requst.json 파일을 준비한다.

{
  "document":{
    "type":"PLAIN_TEXT",
    "content":"Joanne Rowling, who writes under the pen names J. K. Rowling and Robert Galbraith, is a British novelist and screenwriter who wrote the Harry Potter fantasy series."
  },
  "encodingType":"UTF8"
}

이번에는 gcloud 명령이 아닌 curl 명령을 통한 POST 요청을 이용하였다.

curl "https://language.googleapis.com/v1/documents:analyzeEntities?key=${API_KEY}" \
  -s -X POST -H "Content-Type: application/json" --data-binary @request.json

결과는 다음과 같았다.

{
  "entities": [
     {
      "name": "Robert Galbraith",
      "type": "PERSON",
      "metadata": {
        "mid": "/m/042xh",
        "wikipedia_url": "https://en.wikipedia.org/wiki/J._K._Rowling"
      },
      "salience": 0.7980405,
      "mentions": [
        {
          "text": {
            "content": "Joanne Rowling",
            "beginOffset": 0
          },
          "type": "PROPER"
        },
        {
          "text": {
            "content": "Rowling",
            "beginOffset": 53
          },
          "type": "PROPER"
        },
        {
          "text": {
            "content": "novelist",
            "beginOffset": 96
          },
          "type": "COMMON"
        },
        {
          "text": {
            "content": "Robert Galbraith",
            "beginOffset": 65
          },
          "type": "PROPER"
        }
      ]
    },

    ...
  ]
}

문장과 단어에 드러나 있는 감정을 찾아보자.

글이나 문장에서 개체(entity)만 뽑아낼 수 있는 것이 아니라, 이에 담겨 있는 ‘감정’ 까지도 뽑아낼 수 있다. 이를 감정 분석(sentimental analysis)이라고 한다. 이는 제품이나 서비스에 대한 고객들의 느낌이나 평가들을 뽑아낼 수 있는 아주 중요한 방법으로 비즈니스에도 많이 활용된다. 아까와 같이 request.json 파일을 사용하되, 감정 표현이 담겨 있는 문장으로 바꾸어 시도해 보았다. 해리포터에 대한 한 독자의 평가와 생각이 담겨있는 문장이다.

{
  "document":{
    "type":"PLAIN_TEXT",
    "content":"Harry Potter is the best book. I think everyone should read it."
  },
  "encodingType": "UTF8"
}

이번에는 analyzeSentiment 라는 엔드포인트를 이용하여 요청을 주면 된다.

curl "https://language.googleapis.com/v1/documents:analyzeSentiment?key=${API_KEY}" \
  -s -X POST -H "Content-Type: application/json" --data-binary @request.json

결과는 다음과 같다. 감정 분석의 결과는 글 전체와 문장 단위, 두 가지 범위로 제공된다. 감정 분석에서 주목해야 하는 값은 scoremagnitude 이다. score 는 -1.0 과 1.0 사이의 값을 가지며, 0.0 을 기준으로 음수이면 부정적인 감정, 양수이면 긍정적인 감정임을 나타낸다. magnitude 는 감정의 방향과는 별개로 해당 감정이 가지고 있는 강도를 나타낸다. 두 문장으로 이루어진 예제를 살펴보면, 예제 전체에 대해서는 어느정도 긍적적(score = 0.4)이며, 첫 번째 문장은 긍적적(score = 0.8)이며, 두 번째 문장은 중립적인(score = 0.1) 감정을 지니고 있음을 알 수 있다.

{
  "documentSentiment": {
    "magnitude": 0.8,
    "score": 0.4
  },
  "language": "en",
  "sentences": [
    {
      "text": {
        "content": "Harry Potter is the best book.",
        "beginOffset": 0
      },
      "sentiment": {
        "magnitude": 0.7,
        "score": 0.7
      }
    },
    {
      "text": {
        "content": "I think everyone should read it.",
        "beginOffset": 31
      },
      "sentiment": {
        "magnitude": 0.1,
        "score": 0.1
      }
    }
  ]
}

문장 단위뿐만 아니라 개체(entity) 단위에서의 감정 분석도 가능하다. 다음과 같이 ‘스시는 좋아하지만 서비스는 매우 형편이 없었습니다.’라는 두 종류의 감정이 한 문장에 드러나 있는 예제를 가지고 살펴보자.

{
  "document":{
    "type":"PLAIN_TEXT",
    "content":"I liked the sushi but the service was terrible."
  },
  "encodingType": "UTF8"
}

개체 단위에서 감정 분석을 하고 싶다면 요청의 엔드포인트 이름을 analyzeEntitySentiment 으로 바꾸어주면 된다.

curl "https://language.googleapis.com/v1/documents:analyzeEntitySentiment?key=${API_KEY}" \
  -s -X POST -H "Content-Type: application/json" --data-binary @request.json

자, 아래의 결과를 한번 살펴보자. ‘스시’와 ‘서비스’라는 두 개체에 대한 감정이 극(-0.9)과 극(0.9)으로 평가되었음을 잘 알아차렸다.

{
  "entities": [
    {
      "name": "sushi",
      "type": "CONSUMER_GOOD",
      "metadata": {},
      "salience": 0.52716845,
      "mentions": [
        {
          "text": {
            "content": "sushi",
            "beginOffset": 12
          },
          "type": "COMMON",
          "sentiment": {
            "magnitude": 0.9,
            "score": 0.9
          }
        }
      ],
      "sentiment": {
        "magnitude": 0.9,
        "score": 0.9
      }
    },
    {
      "name": "service",
      "type": "OTHER",
      "metadata": {},
      "salience": 0.47283158,
      "mentions": [
        {
          "text": {
            "content": "service",
            "beginOffset": 26
          },
          "type": "COMMON",
          "sentiment": {
            "magnitude": 0.9,
            "score": -0.9
          }
        }
      ],
      "sentiment": {
        "magnitude": 0.9,
        "score": -0.9
      }
    }
  ],
  "language": "en"
}

구문 분석(syntax analysis)도 가능할까?

문장의 구조적 성질을 규칙으로 표현한 것이 ‘문법’인데, 이러한 문법을 이용하여 문장의 구조를 찾아내는 과정을 바로 ‘구문 분석’이라고 한다. 구글 클라우드 Natural Language API 의 세 번째 기능인 analyzeSyntax 를 통하여 이를 수행할 수 있다.

{
  "document":{
    "type":"PLAIN_TEXT",
    "content": "Joanne Rowling is a British novelist, screenwriter and film producer."
  },
  "encodingType": "UTF8"
}

위와 같은 예제를 통해 아래와 같이 명령어를 수행하면,

curl "https://language.googleapis.com/v1/documents:analyzeSyntax?key=${API_KEY}" \
  -s -X POST -H "Content-Type: application/json" --data-binary @request.json

구문 분석의 결과를 볼 수 있고, 문장 중 일부분인 ‘is’ 에 관한 결과를 나타내면 다음과 같다. 영어에서 ‘is’ 는 동사(verb)에 속하고, 수에서는 ‘단수’를 취하며, 시제는 ‘현재’라는 정보들을 partOfSpeech 에서 확인할 수 있다.

{
  "text": {
    "content": "is",
    "beginOffset": 15
  },
  "partOfSpeech": {
    "tag": "VERB",
    "aspect": "ASPECT_UNKNOWN",
    "case": "CASE_UNKNOWN",
    "form": "FORM_UNKNOWN",
    "gender": "GENDER_UNKNOWN",
    "mood": "INDICATIVE",
    "number": "SINGULAR",
    "person": "THIRD",
    "proper": "PROPER_UNKNOWN",
    "reciprocity": "RECIPROCITY_UNKNOWN",
    "tense": "PRESENT",
    "voice": "VOICE_UNKNOWN"
  },
  "dependencyEdge": {
    "headTokenIndex": 2,
    "label": "ROOT"
  },
  "lemma": "be"
},
  1. https://developers-kr.googleblog.com/2018/12/2018-cloud-school.html 

  2. 스터디와는 별개로 정답이 궁금해서 찾아봤다. 1869년 시공, 1883년 완공된 뉴욕에 있는 현수교 라고 한다. 

  3. 영어사전을 통해 ‘특징’ 또는 ‘중요점’ 정도로 해석 가능하다.