본문 바로가기
AI

STT) Naver Cloud(Clova) STT 기술 검토

by 박채니 2024. 12. 10.
SMALL

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.

 

STT (Speech-To-Text) 기술을 사용해야하는 프로젝트가 생겨서 Google, Naver, Azure가 제공하는 STT 서비스를 기술 검토해보았다.

검토사항은 아래와 같다.

  1. 스트리밍 처리가 가능한가
  2. 한국어를 가장 잘 인식할 수 있는가
  3. 가격은 저렴한가

Naver Cloud (Clova)

  • 한국어 / 영어 / 일본어 / 중국어 지원
  • 인식 가능 시간
    • 장문 인식: 최대 2시간 (sync), 최대 6시간 (Batch, async)
    • 단문 인식: 최대 60초
  • 인식 파일 크기
    • 장문인식 : 최대 2GB (빌더), 최대 20GB (API)
    • 단문인식 : 최대 10MB (빌더, API)
  • Clova 공식 홈페이지

가격

출처: Naver Cloud Clova 공식 홈페이지

 

서비스 중 이벤트 탐지 서비스박수나 음악 소리를 인식하는 기능이고, 이 외 서비스는 서비스명을 보면 유추할 수 있을 것이다.

실시간 음성 처리를 해야하므로 장문 인식 유형 & Basic 플랜 & 음성 인식 서비스만을 사용한다고 가정했을 때 가격은 아래와 같다.

  • 1분: 20원
  • 1시간: 1,200원

리소스 한도

별도 리소스 한도에 관한 정보를 찾지는 못했지만 chat streaming api 동시접속 에러 관련 아티클을 발견하였다.

 

"현재는 별도 유량을 제한하는 정책은 없으나, 장문 인식에서는 sync, async 두 가지 방식을 지원하고 있으며, 초당 10개 sync 요청 가능하며 Speech에서 처리 가능한 작업 수는 20개입니다.

따라서 초당 10개씩 2초 간 20개 작업 요청하는 경우 그 이후의 요청에 대해서는 지연/에러가 발생할 수 있습니다."

 

위 아티클과 답변을 토대로 이용량의 한도가 정해져있다는 것을 알 수 있다.

서비스 이용해보기

실시간 스트리밍의 경우, gRPC 방식을 통해서만 접근이 가능하다.

⭐️ gRPC 관련 아티클

https://chanychu.tistory.com/537

 

Anaconda 설치 및 설정 (이미 설치되어 있는 경우 생략 가능)

$ brew install --cask anaconda
$ /opt/homebrew/anaconda3/bin/conda init zsh
$ source ~/.zshrc

 

가상환경 생성

$ conda create -n naverCloudSttSample python=3.13

 

Protoc Compiler 설치 및 준비

$ pip install grpc-tools
$ touch nest.proto

 

nest.proto 파일 작성

syntax = "proto3";
option java_multiple_files = true;
package com.nbp.cdncp.nest.grpc.proto.v1;

enum RequestType {
  CONFIG = 0;
  DATA = 1;
}

message NestConfig {
  string config = 1;
}

message NestData {
  bytes chunk = 1;
  string extra_contents = 2;
}
message NestRequest {
  RequestType type = 1;
  oneof part {
    NestConfig config = 2;
    NestData data = 3;
  }
}

message NestResponse {
  string contents = 1;
}
service NestService {
  rpc recognize(stream NestRequest) returns (stream NestResponse){};
}

 

nest.proto 파일 컴파일 (gRPC 코드 생성)

$ python -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. nest.proto

해당 과정을 거쳤다면 위와 같은 파일이 생성된 것을 확인할 수 있다.

 

패키지 설치

requirements.txt 파일 생성

grpcio==1.67.1
grpcio-tools==1.67.1
protobuf==5.28.3
PyAudio==0.2.14
setuptools==75.1.0
wheel==0.44.0
$ pip install -r requirements.txt

 

예제코드

import grpc
import json

import time
import threading
import nest_pb2
import nest_pb2_grpc
import pyaudio

CLIENT_SECRET = "YOUR SECRET KEY"

RATE = 16000
CHUNK_SIZE = 16000
THRESHOLD = 500
SILENCE_THRESHOLD = 4000

def get_microphone_stream():
    audio = pyaudio.PyAudio()

    try:
        stream = audio.open(
            format=pyaudio.paInt16, 
            channels=1, 
            rate=RATE, 
            input=True, 
            frames_per_buffer=CHUNK_SIZE
        )

        return stream
    except OSError as error:
        print(f"[WARNING]: Unable to access microphone. {error}")


def generate_requests():
    # 초기 설정 요청: 음성 인식 설정
    yield nest_pb2.NestRequest(
        type=nest_pb2.RequestType.CONFIG,
        config=nest_pb2.NestConfig(
            config=json.dumps({"transcription": {"language": "ko"}, "semanticEpd": {"skipEmptyText": True}})
        )
    )

    stream = get_microphone_stream()

    if stream is None:
        raise Exception("[ERROR]: Stream is None.")

    def print_every_second():
        current_time = time.strftime("%H:%M:%S", time.localtime())
        print(f"1초마다 출력됩니다. 현재 시간: {current_time}")
        threading.Timer(1, print_every_second).start()

    print_every_second()

    while True:
        try:
            chunk = stream.read(CHUNK_SIZE, exception_on_overflow=False)

            yield nest_pb2.NestRequest(
                type=nest_pb2.RequestType.DATA,
                data=nest_pb2.NestData(
                    chunk=chunk,
                    extra_contents=json.dumps({"seqId": 0, "epFlag": False})
                )
            )
        except Exception:
            pass

def save_text_to_file(text):
    if text.strip():
        with open("transcription_output.txt", "a", encoding="utf-8") as file:
            file.write(f"{text}\n")

def main():
    # Clova Speech 서버에 대한 보안 gRPC 채널을 설정
    channel = grpc.secure_channel(
        "clovaspeech-gw.ncloud.com:50051",
        grpc.ssl_channel_credentials()
    )
    stub = nest_pb2_grpc.NestServiceStub(channel)  # NestService에 대한 stub 생성
    metadata = (("authorization", f"Bearer {CLIENT_SECRET}"),)  # 인증 토큰과 함께 메타데이터 설정
    responses = stub.recognize(generate_requests(), metadata=metadata)  # 생성된 요청으로 인식(recognize) 메서드 호출

    try:
        # 서버로부터 응답을 반복 처리
        for response in responses:
            response_data = json.loads(response.contents)

            if 'transcription' in response_data:
                transcription = response_data['transcription']
                print(f"Transcript: {transcription['text']}")
                save_text_to_file(transcription['text'])

    except grpc.RpcError as e:
        # gRPC 오류 처리
        print(f"Error: {e.details()}")
    finally:
        channel.close()  # 작업이 끝나면 채널 닫기

if __name__ == "__main__":
    main()

 

공식 홈페이지의 실시간 스트리밍 인식 예제 코드를 참고하여 실시간으로 마이크에 말하는 음성에 대해 텍스트로 변환해주는 python 코드를 짜보았다.

 

요청 파라미터 공식 문서에서 필요한 파라미터를 찾아 추가하여 좀 더 세밀한 작업을 할 수 있게끔 처리할 수도 있다.

 

나는 아래와 같이 음성 인식 설정을 하였다.

인식하는 언어를 한국어로 설정 ("transcription": {"language": "ko"})

인식 결과가 없는 값에 대한 전송을 하지 않도록 ("semanticEpd": {"skipEmptyText": True}) 설정

 

 

위 코드를 보면 알겠지만, nest_pb2.NestRequest 함수를

def generate_requests():
    # 1. 초기 설정 요청: 음성 인식 설정
    yield nest_pb2.NestRequest(
        type=nest_pb2.RequestType.CONFIG,
        config=nest_pb2.NestConfig(
            config=json.dumps({"transcription": {"language": "ko"}, "semanticEpd": {"skipEmptyText": True, "usePeriodEpd": True, "useWordEpd": True, "durationThreshold": 4000}})
        )
    )

    ...
    ...

    while True:
        try:
            chunk = stream.read(CHUNK_SIZE, exception_on_overflow=False)

            # 2. 음성 인식 요청
            yield nest_pb2.NestRequest(
                type=nest_pb2.RequestType.DATA,
                data=nest_pb2.NestData(
                    chunk=chunk,
                    extra_contents=json.dumps({"seqId": 0, "epFlag": False})
                )
            )
        except Exception:
            pass

 

1. 초기 설정 요청: 음성 인식 설정2. 음성 인식 요청 시 사용하는 것을 확인할 수 있는데,

이 때 차이점은 type이 CONFIG이고, DATA인 것이다.

 

  • CONFIG
    "config에 정의한 설정으로 음성 인식을 해주세요"라고 요청
    ⭐️ 반드시 recognize 함수(실시간 스트리밍 인식 API)를 처음 실행 시 전달해줘야 한다.
  • DATA
    음성 인식하려는 chunk 데이터를 전달

위 코드에서도 CONFIG 설정을 먼저 한 후, 마이크 스트리밍 데이터를 CHUNK_SIZE만큼 잘라와 chunk 데이터를 넘겨주어 음성 인식 요청을 한다.

 

가장 중요한 Auth 설정은

def main():
    ...
    ...
    metadata = (("authorization", f"Bearer {CLIENT_SECRET}"),)  # 인증 토큰과 함께 메타데이터 설정
    responses = stub.recognize(generate_requests(), metadata=metadata)  # 생성된 요청으로 인식(recognize) 메서드 호출

 

recognize 함수 호출 시 metadata를 생성하여 같이 넘겨주어 처리해준다.

성능 테스트

샘플동영상을 10분 정도 틀어놓고 얼마나 잘 인식하는지 테스트 해보았다.

결과 값은 아래 파일을 참고하자!

(참고로 테스트를 할 때 durationThreshold: 4000으로 설정하였다. - 4000초 뒤 문장을 끊게끔 설정)

성능도 괜찮았고, 가격도 적당한 편!

아마 Clova를 사용하지 않을까 싶다.

stt_clova_result.txt
0.01MB

LIST

'AI' 카테고리의 다른 글

STT) Azure STT 기술 검토  (0) 2024.12.10
STT) Google Cloud STT 기술 검토  (0) 2024.11.27