trilha: 신호가 없는 트레일에서 새를 귀로 식별하기
요약
trilha는 오프라인 환경에서 녹음된 소리를 분석하여 새의 종류를 식별하고 필드 저널을 작성하는 커맨드 라인 도구입니다. BirdNET 3.0(CPU/ONNX)으로 종을 인식하고, 지리적 범위 모델로 후보군을 좁히며, Gemma 3(Ollama)를 이용해 탐지 결과를 산문 형식의 필드 저널로 변환합니다.
핵심 포인트
- 오프라인에서 작동하는 CLI 도구로, 인터넷 연결이 필수적이지 않습니다.
- BirdNET과 지리적 범위 모델을 결합하여 오탐률을 획기적으로 줄입니다.
- Gemma 3를 활용해 기술적인 탐지 결과를 자연스러운 필드 저널 형식으로 변환합니다.
이것은 Hacktoberfest Open-Source AI Challenge: Week 1에 제출하는 작품입니다 — 테마: Touch Grass.
제가 만든 것
당신은 트레일 위에 있습니다. 수풀 속에서 무언가 소리가 나더니, 두 번, 그리고는 멈춥니다. 그것이 무엇이었는지 알 수 없습니다.
보통의 방법은 휴대폰을 캐노피에 대고 신호 막대가 잡히기를 기다리는 것입니다. 새 관찰(birding)하기 좋은 장소들은 바로 그런 곳들이 아닙니다. 그래서 당신은 6시간 후에 집에 도착하지만, 더 이상 식별할 수 없는 녹음 파일만 남게 되고, 그것은 휴대폰 속에 영원히 남아 있게 됩니다.
trilha는 이 모든 과정을 노트북에서 오프라인으로 처리하는 커맨드 라인 도구입니다. 녹음을 듣고, 들린 것을 이름 붙여주며, 그 외출 기록을 산문 형식의 필드 저널에 작성해 줍니다.
$ trilha listen manha.wav --lat -23.55 --lon -46.63 --place "Parque do Carmo"
manha.wav
...
계정 필요 없음, API 키 필요 없음, 업로드도 필요 없습니다. CPU에서 5.2초 소요.
작동 방식
세 개의 오픈 소스 구성 요소가 각각 잘하는 한 가지 일을 합니다.
귀 (The ear) — BirdNET 3.0을 ONNX를 통해 CPU로 구동. 이 모델은 11,560종의 종을 인식합니다. 오디오를 3초 간격의 창(window)으로 분할하고 각 창을 독립적으로 점수화하기 때문에, 출력물이 단순히 라벨만 제공하는 것이 아니라 새가 _2개의 창에 걸쳐 78초에 처음 들렸음_을 알려줄 수 있습니다.
필터 (The filter) — BirdNET의 범위 모델(range model), 14,082종. 제가 가장 흥미롭다고 생각하고 아무도 데모하지 않는 부분이 바로 이 부분입니다. 위도, 경도, 그리고 연도의 주차를 입력하면, 현재 그곳에 있을 가능성이 있는 후보군으로 범위를 좁혀줍니다. -23.55, -46.63의 40주차라면 11,560종 대신 596종이 됩니다.
이러한 축소 과정이야말로 유용한 답변과 그저 깃털을 가진 모든 것들의 목록 사이의 차이를 만듭니다. 11,560개의 클래스 중에서 선택하도록 요청받은 분류기(classifier)는 상파울루에서 히말라야 새를 기꺼이 제시할 것입니다. 하지만 10월에 실제로 범위 내에 있는 596종 중에서 선택하도록 요청받으면 그렇지 않습니다.
음성(The voice) — Gemma 3를 Ollama로 내 기기에서 구동. 이 기능은 탐지 목록을 일지에 적합한 몇 문장으로 변환합니다. 식별 자체는 건드리지 않습니다.**
이 마지막 지점은 면책 조항이 아니라 엄격한 아키텍처 경계입니다. Ollama가 실행되고 있지 않으면, 해당 메모는 건너뛰어지고 탐지 결과만 출력됩니다. 언어 모델(language model)만이 '취향'을 가진 부분이기 때문에, 이 부분이 없어도 괜찮습니다.
중요했던 두 가지 버그
저는 그것이 작동하는 것을 확인했고 거의 글을 완성할 지경까지 갔었습니다. 그러다 다시 한번 제대로 실행해 보고, 단순히 작동 여부를 확인하는 대신 출력된 내용을 읽어보았습니다.
필드 노트는 다음과 같이 나왔습니다:
Field note
Aqui está a nota de campo em português:
...
한 단락 안에 두 개의 별개의 문제가 있었습니다.
사소했던 문제: 모델이 무엇을 할 것인지 먼저 알린 다음, 마크다운 제목(Markdown heading)을 추가했습니다. 논문 일지처럼 꾸민 터미널에서 **Nota de Campo –**는 노이즈입니다.
실제로 중요했던 문제: _Reserva do Pampa_였습니다. 이 이야기에 그런 장소는 없습니다. 저는 --place를 전달하지 않았기 때문에, 프롬프트는 단순히 좌표(coordinates)인 -23.55, -46.63, 즉 상파울루(São Paulo)를 보냈습니다. 모델은 그 숫자를 읽고, 자신이 그곳을 안다고 판단하여, 보호 구역 이름(reserve name)을 지어내고 잘못된 생물군계(biome)를 부여했습니다. 판파(Pampa)는 1,000km 남쪽에 있습니다.
이것은 프로젝트를 망칠 수 있는 실패 모드였습니다. 필드 일지 전체의 전제는 실제로 어디에 있었는지 기록하는 것이라는 것입니다. 장소 이름 하나가 지어내지면, 그 결과물 전체가 신뢰할 수 없게 됩니다. 단지 그 한 줄만이 아니라, 모든 것이 소급적으로요. 새에 대해서는 정확하지만 땅(ground)에 대해서는 틀린 도구는 아무 도구가 없는 것보다 더 나쁩니다. 왜냐하면 당신은 그것을 믿기 때문입니다.
근본적인 원인은 코드 경로가 아니라 프롬프트 계약(prompt contract)에 있었습니다. 제 원래 SYSTEM 지침에는 이미 '목록에 없는 종은 절대 지어내지 말고, 개체수도 절대로 지어내지 말라'고 되어 있었습니다. 저는 환각 현상(hallucination)을 생각했지만, 오직 분류학(taxonomy)에 대해서만 생각했습니다. 지리적 정보는 제약하지 않았습니다. 그래서 제가 놓치고 있던 규칙을 추가했습니다:
오직 주어진 위치 정보 그대로만 언급하세요. 좌표가 주어졌고 장소 이름이 없다면, 그곳을 명명하지 마세요. 공원, 보호 구역, 지역, 생물 군계 또는 도시를 절대 추측하지 마십시오.
...
같은 녹음 파일이지만, 다음 내용과 함께:
corruíras와 sabiás가 강하게 존재한다는 것은 개방된 숲(matagal aberto) 서식지를 나타내며, 이 지역에 과일나무가 있을 가능성이 있습니다. sabiá-poca의 낮은 신뢰도 식별은 근처에 더 그늘지거나 밀집된 식생 구역이 존재할 수 있음을 시사합니다. 다음으로, 나무 위 높은 곳을 찾아보세요. 왜냐하면 sabiá-laranjeira는 자신의 존재를 알리기 위해 높은 위치에서 나타나는 경향이 있기 때문입니다.
서두(preamble)에는 가짜 장소도 없고, 만들어낸 장소도 없는데도 낮은 신뢰도의 탐지 결과를 낮은 신뢰도로 표시합니다.
그래서 역방향으로 확인해 보았습니다. 왜냐하면 한 방향에서만 작동하는 수정은 해결책이 아니기 때문입니다. `--place
이번 주(Week 1)의 테마는 "자연 속으로 돌아가기(Touch Grass)"이며, 프롬프트에 있는 첫 번째 예시는 문자 그대로 "신호가 없는 트레일에서 새를 식별하는 호출기"입니다. 이것이 바로 trilha-offline이 하는 일입니다. 마감일은 PDT 기준 10월 11일 자정입니다.
코드는 6개 모듈에 걸쳐 677줄이며 CLI는 실행되지만, README가 없고 (그리고 pyproject가 하나를 가리키므로 빌드가 깨지며), git 저장소도 없고, DEV 게시물도 없습니다.
▸ get_challenges 개발(DEV) 챌린지, 활성
입력
DEV challenges, active
출력
Hacktoberfest Open-Source AI Challenge: Week 1 — theme "Touch Grass" — ends 2026-10-12T06:59:00Z. Featured categories include Best Use of Gemma.
▸ bash trilha --version && ollama list
입력
$ trilha --version && ollama list
출력
trilha 0.1.0
gemma3:12b 8.1 GB
gemma3:4b 3.3 GB
...
당신
무엇을 끝내야 할까요?
에이전트
먼저 엔드투엔드로 실행해 보는 것이 좋습니다. 왜냐하면 무언가를 실행하지 않고 작성된 체크리스트는 추측일 뿐이기 때문입니다.
▸ bash trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63 --lang ...
입력
$ trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63 --lang pt --no-journal
출력
596 종(species) 범위 내 · CPU에서 5.2초 소요 · 주차 40
0.91 corruira (Troglodytes musculus) · 처음 감지된 시간: 21초 · 창(window) 9개
...
작동합니다 — 식별은 확실합니다. 하지만 필드 노트에 두 가지 결함이 있습니다.
-
프리앰블 누수(Preamble leak): 노트가 "Aqui esta a nota de campo em portugues:"와 마크다운 제목으로 시작하여, 노트 자체로 시작하지 않습니다.
-
환각된 위치(Hallucinated location) — 두 가지 중 더 심각한 문제입니다. 노트에는 "Reserva do Pampa"라고 이름이 붙어 있습니다. 저는
--place를 전달하지 않았기 때문에, 프롬프트는 빈 좌표(-23.55, -46.63, 즉 상파울루)만 보냈습니다. 모델은 장소 이름을 지어내고 생물군계(biome)까지 잘못 파악했습니다. 자신이 어디에 있었는지 꾸며내는 필드 저널은 자체적인 신뢰도를 무너뜨립니다.
▸ grep place|prompt|system in trilha/narrate.py
입력
place|prompt|system in trilha/narrate.py
출력
필드 노트
카르모 공원(Parque do Carmo)에서 사비아-라란제이라(sabia-laranjeira)의 강한 존재가 확인되어, 이 지역은 열매를 맺는 나무들이 있을 수 있는 개방된 마타갈(matagal) 서식지를 나타냅니다. 사비아-포카(sabia-poca)의 낮은 신뢰도는 근처에 더 그늘지거나 밀집된 식생 구역이 존재할 가능성을 시사합니다. 다음으로, 나무 높은 곳을 찾아보세요. 왜냐하면 사비아-라란제이라가 자신의 존재를 알리기 위해 높은 위치에서 나타나는 경향이 있기 때문입니다.
러퍼스벨리드 트러쉬(Rufous-bellied Thrush), 러퍼스콜어드 스패로우(Rufous-collared Sparrow), 그리고 사우던 랩윙(Southern Lapwing)의 존재는 파르케 두 카르모(Parque do Carmo) 내에 개방된 삼림지와 초원이 혼합된 서식지가 있음을 강력히 시사합니다. 다음으로는 특히 사우던 옐로우스로트(Southern Yellowthroat)의 울음소리에 주의를 기울이세요. 이들은 종종 무성한 하층 식생과 관목을 선호하기 때문입니다.
실제 장소를 사용했습니다. 한 번 더 확인해 봅시다: 메모에서는 독자에게 사우던 옐로우스로트를 듣도록 지시하는데, 이 종이 실제로 탐지 목록에 있나요, 아니면 세 번째 환각인가요?
▸ bash trilha listen samples/sabia-laranjeira-XC112745.mp3 --lat -23.55 --lon -46.63...
입력
$ trilha listen samples/sabia-laranjeira-XC112745.mp3 --lat -23.55 --lon -46.63 --no-note
출력
0.97 Rufous-bellied Thrush
0.94 Rufous-collared Sparrow
0.92 Southern Lapwing
...
0.59에서 존재합니다. 종의 환각은 없었습니다—원래 SYSTEM에 있던
솔직히 말씀드리자면: 저는 아직 실제 트레일에서 이 기능을 사용해 본 적이 없습니다. 이 챌린지는 실제로 해보는 경우 보너스 점수를 제공하며, 데모를 마치 아침 산책처럼 포장하는 것보다 제가 실제로 한 일을 알려드리는 편이 낫습니다.
대신 제가 한 일은 도구에 더 까다롭다고 할 수 있습니다. iNaturalist에는 오디오 녹음, 정확한 좌표, 정확한 날짜, 그리고 커뮤니티가 검증한 종 식별(연구 등급)을 담고 있는 새 관찰 기록으로 가득 찬 공개 API가 있습니다. 이것은 실제 지리 정보가 첨부된 레이블링된 테스트 세트이며—그 내용 중 어느 것도 이 도구를 돋보이게 하기 위해 제가 고른 것이 아닙니다. 저는 여섯 개 국가에서 무작위로 여덟 개의 데이터를 가져와 각각의 실제 좌표와 날짜를 가지고 실행했습니다.
| 어디 | 진실 (Truth) | trilha의 최고 추측 (top guess) | |
|---|---|---|---|
| 🇧🇷 Parque Nacional das Emas | Melanopareia torquata | Collared Crescentchest 0.98 | ✅ |
| ... |
첫 번째 추측에서 여덟 개 중 여섯 개가 맞았고, 최고 세 가지 추측에서는 여덟 개 중 일곱 개가 맞았습니다. CPU 사용 시 각 항목당 3.2–4.9초가 소요되었습니다. 범위 필터는 매번 현장에서 후보군을 다르게 축소했습니다—1월 브라질 세하두(cerrado)에서는 592종, 11월 말 우크라이나 서부에서는 232종이었습니다—이는 전체 메커니즘이 의도대로 작동하고 있음을 의미합니다.
성공하지 못한 두 가지 경우가 흥미롭습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기