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:
A precisão de acerto foi de 98% para a letra individual e 90% para o CAPTCHA inteiro (as 6 letras conjuntamente).
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.
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.
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:
0
e 1
do mesmo tamanho do áudio em que o 1
indica se o valor é igual a algum valor dos 300 primeiros valores.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).
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
skmhwv.png
- a imagem do CAPTCHA
skmhwv.wav
- o áudio do CAPTCHA
skmhwv.rds
- um data.frame com o seguinte layout:
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 |
som
: Amplitude observada no instante de tempo.tempo
: Identificador do instante de tempo.w0
: “Índice” de ruído. A média móvel citada acima. Quanto maior, mais provável que o instante de tempo faça parte de um período sem a fala da letra.final
: Definição final dos períodos do som. Espera-se que 6 desses períodos correspondam às 6 letras do captcha.final_diff
: Identificação dos períodos do som. As identificações vão de 0 a 12 e os números ímpares correspondem às 6 letras do captcha.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.
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).
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…
Kappa na base de teste: 98,0%
Acurácia na base de teste: 98,1% (implicando em ~90% de acerto no CAPTCHA inteiro).
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.
Gareth James, Daniela Witten, Trevor Hastie, Robert Tibshirani. An Introduction to Statistical Learning : with Applications in R. New York :Springer, 2013.
Max Kuhn, Kjell Johnson. Feature Engineering and Selection: A Practical Approach for Predictive Models Chapman & Hall/CRC Data Science Series. 1st Edition, 2019.