1 Introdução

CAPTCHAs (Completely Automated Public Turing test to tell Computers and Humans Apart) são as imagens que restrigem o acesso direto por robôs em páginas da internet. Elas têm a característica de letras e números distorcidos para o usuário tentar decifrar o que está escrito nelas. No caso do CAPTCHA da Receita Federal, o texto é composto de 6 letras ou números e não importa se é letra maiúscula ou minúscula.

Além da imagem, o site oferece a versão de audio do CAPTCHA. O computador já consegue decifrar o texto “MJGPTJ” usando apenas a imagem. Esse relatório irá mostrar como fazer o computador decifrar usando o audio por meio de construção manual de características do som (features) e para alimentar um modelo preditivo. A técnica de modelagem preditiva escolhida foi Random Forest.

As etapas foram:

  1. Coleta de 590 CAPTCHAs da internet;
  2. Anotação das respostas de cada um dos 590 CAPTCHAs;
  3. Segmentação das 6 letras;
  4. Modelagem;

A precisão de acerto foi de 98% para a letra individual e 90% para o CAPTCHA inteiro (as 6 letras conjuntamente).

2 Sobre os dados

O dado bruto é um arquivo .wav. A parte desse arquivo que interessa para nós será um vetor de inteiros. O exemplo abaixo mostra um audio de tamanho 36156 ou seja, 36.156 valores inteiros.

audio <- ler_audio("captchaexemplo.wav") 
head(audio, 10)
# 4883  270  257   -1 4625 2829 4616  769   -1 3081    
length(audio)
# 36156

No estado original, o vetor possui o audio das 6 letras pronunciadas em sequência. A segmentação desse audio em 6 fatias é uma etapa importante tratada na seção 4.

No final do processo, teremos um vetor de inteiros associado a uma letra ou algarismo. Então 590 CAPTCHAs fornecerão 590 * 6 observações para treinar o modelo preditivo.

3 Coleta dos dados

Os dados foram coletados por simples requisições GET. O endereço http://www.receita.fazenda.gov.br/pessoajuridica/cnpj/cnpjreva/captcha/gerarCaptcha.asp

fornece a imagem e o endereço

http://www.receita.fazenda.gov.br/pessoajuridica/cnpj/cnpjreva/captcha/gerarSom.asp

fornece o áudio. As imagens foram utilizadas para auxiliar na parte de Anotação das respostas (associar a letra ao som). No R esse processo fica fácil conforme ilustra o código abaixo:

url_imagem <- 'http://www.receita.fazenda.gov.br/pessoajuridica/cnpj/cnpjreva/captcha/gerarCaptcha.asp'
url_audio <- 'http://www.receita.fazenda.gov.br/pessoajuridica/cnpj/cnpjreva/captcha/gerarSom.asp'

imagem <- httr::GET(url_imagem)
audio  <- httr::GET(url_audio)

Ao rodar esse código N vezes, serão baixados N imagens e seus respectivos áudios. Efetivamente foram feitas 590 rodadas e os arquivos foram armazenados em uma pasta única.

4 Segmentação das letras

A segmentação das letras é necessária para conseguirmos rotulá-las posteriormente com uma letra ou um algarismo. Uma vez segmentadas e rotuladas, os dados estarão prontos para modelagem preditiva (que hoje está mais comum se chamar de machine learning supervisionado de classificação).

Em geral esse procedimento é bastante complexo e difícil, porém no caso do áudio da Receita Federal o som que aparece entre as letras faladas é de fácil detecção pois os valores inteiros dessas partes são sempre os mesmos (e não é aleatório, como imagina-se de início). Então fica trivial tirar proveito dessa particularidade.

Assim, o processo de segmentação adotou a seguinte estratégia:

  1. coleta-se os primeiros 300 valores do áudio (pois são sempre “não letras”).
  2. cria-se um vetor de 0 e 1 do mesmo tamanho do áudio em que o 1 indica se o valor é igual a algum valor dos 300 primeiros valores.
  3. passa-se uma média móvel neste vetor de indicadores feito em 2.
  4. faz-se o corte segundo uma nota de corte.

A linha vermelha no gráfico abaixo é a média móvel mencionado no passo 3. Assume-se que não é letra a região que apresenta um valor alto dessa média. Ou seja, todo ponto que ficar acima da nota de corte assume-se que não faz parte do som da letra.

Podem ocorrer erros de segmentação nessa etapa (quando o som da letra tem som que lembra o ruído). Esses casos são detectados quando a segmentação devolve um valor diferente de 6 regiões de letras. Uma vez identificado o áudio, aplica-se segmentação manual. Ao todo, por volta de 13 áudios foram segmentados manualmente (importante notar que a predição bem sucedida depende de uma segmentação bem sucedida).

5 Anotação das respostas

Para fazer as anotações, foi construído um aplicativo utilizando o framework Shiny do R para auxiliar na tarefa de associar as letras e algarismos com as ondas sonoras. A imagem abaixo é a interface do anotador.

Os arquivos foram gravados com a resposta, ou seja, do CAPTCHA com a escrita “skmhwv” foram gerados os arquivos

arq_aud som tempo w0 final final_diff eh_letra
../captchaexemplo.wav 4883 1 150 1 0 FALSE
../captchaexemplo.wav 270 2 150 1 0 FALSE
../captchaexemplo.wav 257 3 150 1 0 FALSE
../captchaexemplo.wav -1 4 150 1 0 FALSE
../captchaexemplo.wav 4625 5 150 1 0 FALSE
../captchaexemplo.wav 2829 6 150 1 0 FALSE

6 Modelagem

Na construção do modelo preditivo, alimentou-se uma random forest com os sons e as letras/algarismos pareados. A base possui 3.540 vetores de sons rotulados.

6.1 Variável resposta

A variável resposta é a letra/algarismo e as 35 categorias possíveis são de a a z e de 1 a 9 (não tem o zero).

6.2 Variáveis explicativas

Foram extraídas 20 características do vetor do som de cada letra:

  • comprimento: tamanho do som (ou comprimento do vetor de amplitudes).
  • ataque: o instante da amplitude máxima.
  • ataque2: o instante de amplitude máxima após o ataque.
  • ataque3: o instante de amplitude máxima antes do ataque.
  • som_letra_dp_q[1,2,3]: desvios padrão das amplitudes nos quartis 1, 2 e 3.
  • som_letra_abs_dp_q[1, 2, 3]: desvios padrão do absoluto das amplitudes nos quartis 1, 2 e 3.
  • som_letra_abs_dp: desvio padrão do absoluto das amplitudes (do som inteiro).
  • som_letra_max_q[1, 2, 3]: amplitude máxima nos quartis 1, 2 e 3.
  • som_letra_abs_max_q[1, 2, 3]: amplitude absoluta máxima nos quartis 1, 2 e 3.
  • som_letra_dp_p2: desvio padrão da amplitude no período entre os percentis 35 e 40.
  • som_letra_abs_dp_p2: desvio padrão do absoluto da amplitude no período entre os percentis 35 e 40.
  • som_letra_max_p2: amplitude máxima no período entre os percentis 35 e 40.
glimpse(base_de_treino)

# Observations: 3,540
# Variables: 22
# $ id                   <int> 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, …
# $ y                    <chr> "1", "b", "q", "h", "t", "j", "1", "…
# $ comprimento          <int> 2300, 3345, 3133, 4274, 2994, 5199, …
# $ ataque               <int> 463, 1054, 865, 2151, 710, 1679, 322…
# $ ataque2              <int> 37, 41, 36, 43, 30, 41, 37, 87, 41, …
# $ ataque3              <int> 370, 1004, 821, 376, 672, 1630, 229,…
# $ som_letra_dp_q1      <dbl> 11654.405, 4213.822, 3352.329, 6104.…
# $ som_letra_dp_q2      <dbl> 8857.874, 9048.895, 9038.450, 6405.0…
# $ som_letra_dp_q3      <dbl> 4805.608, 5983.047, 4120.875, 4949.1…
# $ som_letra_abs_dp_q1  <dbl> 5464.428, 2486.018, 2366.673, 3951.7…
# $ som_letra_abs_dp_q2  <dbl> 4794.609, 5345.669, 4258.964, 3709.1…
# $ som_letra_abs_dp_q3  <dbl> 3017.748, 3569.497, 2531.496, 2970.6…
# $ som_letra_abs_dp     <dbl> 5146.383, 4100.117, 3733.390, 3355.4…
# $ som_letra_max_q1     <int> 18962, 9371, 14791, 23262, 26258, 19…
# $ som_letra_max_q2     <int> 16258, 24694, 19459, 21593, 25334, 2…
# $ som_letra_max_q3     <int> 5495, 17383, 12502, 24512, 13602, 20…
# $ som_letra_abs_max_q1 <int> 19214, 9371, 15382, 23262, 26258, 19…
# $ som_letra_abs_max_q2 <int> 17536, 24694, 19459, 21593, 25334, 2…
# $ som_letra_abs_max_q3 <int> 12947, 17383, 12502, 24512, 13602, 2…
# $ som_letra_dp_p2      <int> 11567, 22146, 16064, 12355, 22989, 2…
# $ som_letra_abs_dp_p2  <int> 15394, 22146, 16064, 14694, 22989, 2…
# $ som_letra_max_p2     <int> 11567, 22146, 16064, 12355, 22989, 2…

6.3 Ajuste do modelo

  • Base de treino e teste: 70%/30%.
  • Algoritmo: Random Forest.
  • Métrica de avaliação: Kappa.
  • Método de reamostragem: Repeated Cross-Validation com 5 folds e 4 repetições.

6.4 Desempenho

  • Kappa na base de teste: 98,0%

  • Acurácia na base de teste: 98,1% (implicando em ~90% de acerto no CAPTCHA inteiro).

7 Conclusão

Construir features na mão tem potencial de resolver problemas práticos de machine learning envolvendo som. A praticidade do Random Forest compensa o trabalho manual da confecção das características. O próximo passo seria explorar técnicas de redes neurais que permitem dispensar a criação explícita de features.

8 Referências