NAVER CLOVA Speech
네이버의 CLOVA Speech는 NEST (Neural End-to-end Speech Transcriber) 음성 인식 기술을 통해, 정형화되지 않은 발화를 인식하여 텍스트로 바꿔주는 서비스 입니다. 여기서 NEST란, end-to-end 방식으로 학습하여 정형화되지 않은 길고 복잡한 문장에 대해서도 정확한 음성 인식을 가능케하는 기술을 말합니다. CLOVA Speech는 음성 명령 인식 기능, 음성-텍스트 변환 받아쓰기 기능, 화자 인식 기능을 제공합니다. 저는 화자분할 (Speaker Diarization) 서비스를 만들기 위해, 최소 1분 이상의 장문 발화 인식이 가능한 CLOVA Speech API를 사용하기로 결정했습니다.
CLOVA Speech API 사용 방법
1. 회원가입
네이버 API를 사용하는 것이기 때문에, 아래 링크를 타고 들어가 클라우드 플랫폼 회원가입 절차를 밟아야 합니다.
순서대로, API 이용 신청 페이지, CLOVA Speech 문서를 첨부하였습니다.
2. 이용 신청
버킷에 있는 데이터를 CLOVA Speech에 넣는 방식으로 동작하기 때문에, CLOVA Speech와 Object Storage 모두 이용 신청을 해야 합니다.
3. 버킷 / 도메인 생성
CLOVA Speech를 정상적으로 구동하기 위해, Objective Storage 버킷을 생성해서 인식 대상과 결과 파일의 경로를 지정해줘야 합니다. 먼저, Object Storage 콘솔에 들어가 버킷을 생성합니다. API를 이용해 어떤 서비스를 만들지에 따라 세부 옵션을 선택해 주면 됩니다. 아래 파란색 생성 버튼을 눌러주세요!
CLOVA Speech 콘솔에서도 도메인을 생성해줘야 합니다. 도메인 선택 시, 장문 인식으로 할지 단문 인식으로 할지 결정해야 하는데.. 저는 음성 인식 기반 화자별 대화 스크립트 (e.g. 회의록) 생성 서비스를 개발하는 것이 목표이기 때문에 장문 인식 도메인을 선택했습니다.
4. 코드 입력 및 실행
API를 호출하기에 앞서, 화자분할 및 음성인식을 하기 위해, 테스트 데이터로 AI Hub의 '민원(콜센터) 질의-응답 데이터' 샘플을 사용하였습니다.
CLOVA Speech API를 호출하는 방법으로는 총 3가지가 있습니다.
1. 외부에서 접속 가능한 URL로 요청
2. object storage에 오디오 파일을 저장해 사용
3. local 환경에서 파일 업로드를 통한 요청
저는 이중에서 제일 쉬운 마지막 방법을 선택했습니다. local 환경에서 오디오 파일을 업로드하고 공식 문서에서 가져온 아래 request 코드를 구동하면, 클로바 엔진에서 처리한 결과값을 json 포맷으로 받아볼 수 있습니다. API 호출을 위해 필요한 secret key는 'CLOVA Speech 콘솔창 → 빌더 실행 → 설정' 에서 확인할 수 있습니다. request 코드에 대한 자세한 사항은 공식 문서에서 참고해주세요 :)
* Request
import requests
import json
class ClovaSpeechClient:
# Clova Speech invoke URL
invoke_url = ''
# Clova Speech secret key
secret = ''
def req_url(self, url, completion, callback=None, userdata=None, \
forbiddens=None, boostings=None, wordAlignment=True, \
fullText=True, diarization=None, sed=None):
request_body = {
'url': url,
'language': 'ko-KR',
'completion': completion,
'callback': callback,
'userdata': userdata,
'wordAlignment': wordAlignment,
'fullText': fullText,
'forbiddens': forbiddens,
'boostings': boostings,
'diarization': diarization,
'sed': sed,
}
headers = {
'Accept': 'application/json;UTF-8',
'Content-Type': 'application/json;UTF-8',
'X-CLOVASPEECH-API-KEY': self.secret
}
return requests.post(headers=headers,
url=self.invoke_url + '/recognizer/url',
data=json.dumps(request_body).encode('UTF-8'))
def req_object_storage(self, data_key, completion, callback=None, \
userdata=None, forbiddens=None, boostings=None,wordAlignment=True, \
fullText=True, diarization=None, sed=None):
request_body = {
'dataKey': data_key,
'language': 'ko-KR',
'completion': completion,
'callback': callback,
'userdata': userdata,
'wordAlignment': wordAlignment,
'fullText': fullText,
'forbiddens': forbiddens,
'boostings': boostings,
'diarization': diarization,
'sed': sed,
}
headers = {
'Accept': 'application/json;UTF-8',
'Content-Type': 'application/json;UTF-8',
'X-CLOVASPEECH-API-KEY': self.secret
}
return requests.post(headers=headers,
url=self.invoke_url + '/recognizer/object-storage',
data=json.dumps(request_body).encode('UTF-8'))
def req_upload(self, file, completion, callback=None, userdata=None, \
forbiddens=None, boostings=None, wordAlignment=True, \
fullText=True, diarization=None, sed=None):
request_body = {
'language': 'ko-KR',
'completion': completion,
'callback': callback,
'userdata': userdata,
'wordAlignment': wordAlignment,
'fullText': fullText,
'forbiddens': forbiddens,
'boostings': boostings,
'diarization': diarization,
'sed': sed,
}
headers = {
'Accept': 'application/json;UTF-8',
'X-CLOVASPEECH-API-KEY': self.secret
}
print(json.dumps(request_body, ensure_ascii=False).encode('UTF-8'))
files = {
'media': open(file, 'rb'),
'params': (None, json.dumps(request_body, \
ensure_ascii=False).encode('UTF-8'), \
'application/json')
}
response = requests.post(headers=headers, url=self.invoke_url \
+ '/recognizer/upload', files=files)
return response
if __name__ == '__main__':
res = ClovaSpeechClient().req_upload(file='/data/media.mp3', \
completion='sync')
print(res.text)
* Response
{
"result": "COMPLETED",
"message": "Succeeded",
"token": "d3bea166039e486abbb90e4a84c3b3a5",
"version": "ncp_v2_v2.3.0-aa6cd8d-20231205_231211-3cf30bfc_v0.0.0_",
"params": {
"service": "ncp",
"domain": "general",
"lang": "enko",
"completion": "sync",
"callback": "",
"diarization": {
"enable": true,
"speakerCountMin": -1,
"speakerCountMax": -1
},
"sed": {
"enable": true
},
"boostings": [
{
"words": "안녕하세요, 테스트"
}
],
"forbiddens": "",
"wordAlignment": true,
"fullText": true,
"noiseFiltering": true,
"resultToObs": false,
"priority": 0,
"userdata": {
"_ncp_DomainCode": "NEST",
"_ncp_DomainId": 1,
"_ncp_TaskId": 55442,
"_ncp_TraceId": "36a75ce98ec342d8a8c8fe9191cec343",
"id": 1
}
},
"progress": 100,
"keywords": {},
"segments": [
{
"start": 5870,
"end": 8160,
"text": "서울 수영장입니다.",
"confidence": 0.9626975,
"diarization": {
"label": "2"
},
"speaker": {
"label": "2",
"name": "B",
"edited": false
},
"words": [
[
5871,
6730,
"서울"
],
[
6860,
7530,
"수영장입니다."
]
],
"textEdited": "서울 수영장입니다."
},
{
"start": 8160,
"end": 12950,
"text": "입장료가 얼마예요? 5천 원이에요. 감사합니다.",
"confidence": 0.8835926,
"diarization": {
"label": "1"
},
"speaker": {
"label": "1",
"name": "A",
"edited": false
},
"words": [
[
8161,
9220,
"입장료가"
],
[
9390,
10020,
"얼마예요?"
],
[
10410,
10640,
"5천"
],
[
10710,
11140,
"원이에요."
],
[
11910,
12500,
"감사합니다."
]
],
"textEdited": "입장료가 얼마예요? 5천 원이에요. 감사합니다."
}
],
"text": "서울 수영장입니다. 입장료가 얼마예요? 5천 원이에요. 감사합니다.",
"confidence": 0.9071357,
"speakers": [
{
"label": "1",
"name": "A",
"edited": false
},
{
"label": "2",
"name": "B",
"edited": false
}
],
"events": [
{
"type": "music",
"label": "music",
"labelEdited": "music",
"start": 1400,
"end": 5000
}
],
"eventTypes": [
"music"
]
}
인식 결과는 json 포맷으로 나오는데 각각의 key-value 쌍을 참고하여 request 코드를 아래와 같이 수정하면, 좀 더 예쁘게 출력할 수 있습니다.
if __name__ == '__main__':
res = ClovaSpeechClient().req_upload(file='media2.mp3', completion='sync')
result = res.json()
# 화자별 인식 결과 segment 추출
segments = result.get('segments', [])
speaker_segments = []
for segment in segments:
speaker_label = segment['speaker']['label']
text = segment['text']
speaker_segments.append({'speaker': speaker_label, 'text': text})
# 화자별 인식 결과 segment 출력
for speaker_segment in speaker_segments:
speaker_label = speaker_segment['speaker']
text = speaker_segment['text']
print(f'Speaker {speaker_label}: {text}')
인식 결과
'AI 구현 > 환경 세팅' 카테고리의 다른 글
[WSL] wget: unable to resolve host address 에러 해결 (0) | 2025.01.12 |
---|---|
[Docker] NVIDIA Container Toolkit 설치 과정 (0) | 2025.01.09 |
맥북 M1 pro에 Tensorflow, Keras 개발 환경 세팅하기 (1) | 2023.10.30 |
M1 Mac에서 Image data augmentation 오류 해결 (0) | 2023.10.01 |
google-colab import 시 오류 해결(AttributeError: module 'IPython.utils.traitlets' has no attribute 'Unicode') (0) | 2023.09.30 |