-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcap-experimento.tex
More file actions
359 lines (253 loc) · 36.5 KB
/
cap-experimento.tex
File metadata and controls
359 lines (253 loc) · 36.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
%% ------------------------------------------------------------------------- %%
\chapter{Metodologia do Experimento}
\label{cap:experimento}
Neste capítulo apresentamos as etapas do processo de treinamento e avaliação das redes neurais. De acordo com \citeonline{nndesign:2014:pratical-training-issues}, o treinamento e avaliação de uma rede neural são compostas pelas seguintes etapas:
\begin{itemize}
\item Coleta e pré-processamento dos dados
\item Definição do tipo de rede neural e arquitetura
\item Seleção do algoritmo de treinamento
\item Treinamento
\item Análise do desempenho da rede neural
\item Utilização do modelo
\end{itemize}
Conforme a Figura~\ref{fig:training-neural-network-cyclic}, este é um processo cíclico e iterativo que inicia-se com a coleta e pré-processamento dos dados e termina com a utilização do modelo. Neste capítulo apresentamos as seguintes etapas deste processo:
\begin{itemize}
\item Coleta e pré-processamento dos dados
\item Treinamento
\item Análise do desempenho da rede neural
\end{itemize}
As outras etapas já foram discutidas em outros capítulos ou estão fora do escopo do presente trabalho. A etapa de ''definição do tipo de rede neural e arquitetura'' já foi discutida no Capítulo~\ref{cap:abordagem} e a ''utilização do modelo'' está fora do escopo do trabalho neste momento. Com relação à etapa de ''seleção do algoritmo de treinamento'', optamos por utilizar o algoritmo de otimização \emph{Adam} para aprendizagem, seguindo os experimentos feitos por \citeonline{yao-2018}.
\begin{figure}[H]
\centering
\caption{Ilustração do processo de treinamento e avaliação de uma rede neural. Figura adaptada do \citeonline{nndesign:2014:pratical-training-issues}.}
\includegraphics[width=1\textwidth]{figuras/cap-experimento/neural_network_training_lifecycle_dissertation.pdf}
\source{Elaborado pelo autor.}
\label{fig:training-neural-network-cyclic}
\end{figure}
\section{Coleta e pré-processamento dos dados}
\label{sec:coleta-pre-processamento-dos-dados}
Conforme \citeonline{nndesign:2014:pratical-training-issues}, o processo de treinamento e avaliação de uma rede neural inicia-se com a coleta e pré-processamento dos dados. Em nosso caso, não houve coleta, pois utilizamos parte do conjunto de dados disponibilizados por \citeonline{yao-2018}. Esses dados estão sob a licença \href{https://creativecommons.org/licenses/by/4.0/}{Creative Commons}, onde o uso é permitido desde que faça referência ao trabalho dos pesquisadores. Ressaltamos que utilizamos parte do conjunto, pois os pesquisadores coletaram questões e trechos de código-fonte em Python e SQL do site \Gls{sof}. Em nosso trabalho, optamos apenas por utilizar o conjunto de dados em Python.
Utilizamos os dados disponibilizados por \citeonline{yao-2018}, pois esses dados contém duas características importantes: Maior variabilidade das questões e trechos de código-fonte em relação a amostras anteriores \cite{iyer-etal-2016-summarizing, Allamanis-bimodal-source-code-natural-language:2015} e questões do tipo \textit{how-to-do-it}. A amostra coletada por \citeonline{yao-2018} apresenta 30\% das questões com mais de um trecho de código-fonte como solução, enquanto os trabalhos de \citeonline{iyer-etal-2016-summarizing} e \citeonline{Allamanis-bimodal-source-code-natural-language:2015} coletaram questões que apresentavam apenas um trecho de código como resposta. As questões do tipo \textit{how-to-do-it} costumam ser mais diretas e exibir um trecho de código-fonte como solução. Conforme experimentos realizados por \citeonline{yao-2018}, as questões do tipo \textit{how-to-do-it} auxiliam o modelo a correlacionar os trechos de código-fonte às intenções de busca do usuário.
\subsection{Conjunto de dados}
\label{sec:conjunto-dados}
O conjunto de dados disponibilizado por \citeonline{yao-2018} é formado por $\bm{147.546}$ pares de questões em inglês e trechos de código-fonte em Python e $\bm{119.519}$ em SQL. Eles foram coletados do site \Gls{sof} e contemplam questões dos anos de 2008 a 2016\footnote{\url{https://github.com/LittleYUYU/StackOverflow-Question-Code-Dataset}}. Podemos dividi-los em 3 (três) subconjuntos distintos:
\begin{itemize}
\item N1: Questões coletadas do \Gls{sof} que contêm apenas um trecho de código-fonte na descrição da resposta
\item N2: Questões que apresentam mais de um trecho de código-fonte na descrição
\item N3: Pares de questões e trechos de código-fonte anotados manualmente
\end{itemize}
\begin{figure}[H]
\centering
\caption[Divisão das amostras de pares de questões e trechos de código.]{Divisão das amostras de pares de questões e trechos de código disponibilizados por \citeonline{yao-2018}: $N_{1} = \{(q_{i}, c_{i}^{+})\;,\; \forall\; q_{i},\;\nexists\; c_{j}^{+}\; |\; c_{j}^{+} \neq c_{i}^{+}\; \text{para}\; i \neq j,\; 1 \leq i,j \leq |N_{1}| \}$, $N_{2} = \{(q_{i}, c_{i}^{+})\;,\; \exists\; q_{i}\; | \;\exists\; c_{j}^{+} \neq c_{i}^{+}\; \text{para} \; i \neq j,\; 1 \leq i,j \leq |N_{2}| \}$, $N_{3} = \{(q_{i}, c_{i}^{+}),\; \forall\; c_{i}^{+} \text{foi anotado manualmente}, 1 \leq i \leq |N_{3}| \}$. Onde $N_{1} \cap N_{2} \cap N_{3} = \varnothing$, $q_{i} \in \mathbb{Q}$ e $c_{i}^{+}, c_{j}^{+} \in \mathbb{C}$. E $c_{i}^{+}, c_{j}^{+}$ são respostas anotadas como corretas. $\mathbb{Q}$ e $\mathbb{C}$ representam o conjunto de questões e trechos de código, respectivamente.}
\includegraphics[width=0.6\textwidth]{figuras/cap-experimento/distinct-subsets-yao-sample.pdf}
\source{Elaborado pelo autor.}
\label{fig:distinct-subset-python-pair-question-code}
\end{figure}
A ilustração da divisão dos subconjuntos pode ser visualizada na Figura~\ref{fig:distinct-subset-python-pair-question-code}. Um ponto importante a ser ressaltado são os casos em que há mais de um trecho de código-fonte na descrição da resposta, pois pode haver algum trecho que não é necessariamente uma solução para a questão. No exemplo a seguir, os trechos \emph{T2} e \emph{T4} não são soluções para a dúvida do usuário. O trecho \emph{T2} apenas inicializa um array e o trecho \emph{T4} mostra a saída do comando \textit{print} para o vetor já convertido\footnote{Site: \url{https://stackoverflow.com/questions/34349762/convert-2d-numpy-array-to-string} Esta questão apresenta 6 trechos de código-fonte, mas para melhor visualização, optamos por colocar apenas 4 trechos em nosso exemplo\label{foot:exemplo-stackoverflow-mais-de-um-trecho}}.
\begin{tcolorbox}[colframe=orange!75!black,colback=gray!15!white,fonttitle=\bfseries,adjusted title=\large{Título da questão: convert 2d numpy array to string}~\ref{foot:exemplo-stackoverflow-mais-de-um-trecho}]
\begin{mypythongreen}{Trecho T1: A one-liner will do:}
b = '\n'.join('\t'.join('%0.3f' %x for x in y) for y in a)
\end{mypythongreen}
\begin{mypythonred}{Trecho T2: Using a simpler example:}
>>> a = np.arange(25, dtype=float).reshape(5, 5)
>>> a
array([[ 0., 1., 2., 3., 4.],
[ 5., 6., 7., 8., 9.],
[ 10., 11., 12., 13., 14.],
[ 15., 16., 17., 18., 19.],
[ 20., 21., 22., 23., 24.]])
\end{mypythonred}
\begin{mypythongreen}{Trecho T3: is equivalent to:}
res = []
for y in a:
res.append('\t'.join('%0.3f' %x for x in y))
b = '\n'.join(res)
\end{mypythongreen}
\begin{mypythonred}{Trecho T4: prints this:}
0.000 1.000 2.000 3.000 4.000
5.000 6.000 7.000 8.000 9.000
10.000 11.000 12.000 13.000 14.000
15.000 16.000 17.000 18.000 19.000
20.000 21.000 22.000 23.000 24.000
\end{mypythonred}
\end{tcolorbox}
\citeonline{yao-2018} criaram um modelo para classificar quais trechos de código-fonte da descrição da resposta são soluções a uma dada questão. Inicialmente foi feita uma anotação manual em uma amostra de aproximadamente 2000 pares de questões e trechos de código-fonte, marcando com 1 os trechos de código que são solução e 0, caso contrário. Posteriormente, uma rede neural recorrente foi treinada a classificar os trechos de código-fonte como solução às perguntas, obtendo um resultado de F1 de $0,916$ e acurácia de $0,911$ em seus testes. Por fim, os pesquisadores utilizaram este modelo para classificar automaticamente o conjunto de pares $N_{2}$ e disponibilizaram os dados anotados e divididos conforme a Tabela~\ref{table:summary-training-data-yao-staqc}.
\begin{table}[h]
\centering
\caption[Divisão do conjunto de dados.]{Divisão do conjunto de dados disponibilizado por \citeonline{yao-2018}. O conjunto formado por "Trechos de código-fonte anotados automaticamente" contém questões que tem mais de um trecho de código-fonte por resposta. Quando há mais de um trecho de código-fonte na descrição da resposta, pode haver algum trecho que não seja solução. Nesse caso, \citeonline{yao-2018} criaram um framework para anotá-los automaticamente e obtiveram F1 de $0,916$ e acurácia de $0,911$ em seus resultados de classificação automática das respostas corretas. }
\begin{tabular}{ p{16em} P{10em} P{10em} }
\hline
& \multicolumn{2}{c}{\textbf{Questão}}\\
\hline
\textbf{Código-fonte} & \textbf{Python} & \textbf{SQL} \\
\hline
$N_{1}$: Apenas 1 trecho de código na descrição da resposta & $85.294$ & $75.637$ \\
$N_{2}$: Trechos de código-fonte anotados automaticamente & $60.083$ & $41.826$ \\
$N_{3}$: Trechos de código-fonte anotados manualmente & $2.169$ & $2.056$ \\
\hline
\textbf{Total} & $\bm{147.546}$ & $\bm{119.519}$\\
\hline
\end{tabular}
\source{Tabela do artigo \citeonline{martins2020concra}.}
\label{table:summary-training-data-yao-staqc}
\end{table}
\subsection{Conjunto de dados para avaliação e treinamento}
\label{sec:conjunto-dados-avaliacao-treinamento}
Com relação ao conjunto de dados, utilizamos os conjuntos $N_{2}$ e $N_{3}$ para treinamento e avaliação, respectivamente. Para o treinamento, optamos por utilizar o conjunto $N_{2}$, pois apresenta uma variabilidade maior, já que em torno de 27\% das questões contém mais de um trecho de código-fonte anotados como correto, ou seja, 27\% das perguntas tem mais de uma opção de solução. No caso da avaliação, utilizamos o conjunto $N_{3}$, que apresenta uma maior qualidade e acurácia nos dados, pois eles foram anotados manualmente. A quantidade de pares presentes nas amostras $N_{2}$ e $N_{3}$ podem ser visualizadas na Tabela~\ref{table:divisao-amostras}. Lembrando que $N_{3} = DEV \cup EVAL$. E $N_{2} \cap N_{3} = \varnothing$.
\begin{table}[h]
\centering
\caption[Divisão das amostras para treinamento e avaliação.]{Divisão das amostras para treinamento e avaliação. O conjunto de dados é formado por pares $<q_{i}, c_{i}^{+}>$, onde $q_{i}$ é uma questão e $c_{i}^{+}$ é um trecho de código-fonte anotado como correto. O conjunto formado por pares anotados manualmente foi dividido em DEV e EVAL conforme o procedimento descrito por \citeonline{iyer-etal-2016-summarizing}. É possível visualizar uma ilustração deste procedimento nas Figuras \ref{fig:evaluation-process} e \ref{fig:final-evaluation-process}. }
\begin{tabular}{ p{5cm} r }
\hline
\textbf{Amostras} & \textbf{Quantidade de pares $<q_{i}, c_{i}^{+}>$}\\
\hline
$N_{2} = \text{Treinamento}$ & $60.083$\\
$N_{3} \supset \text{DEV}$ & $1.085$ \\
$N_{3} \supset \text{EVAL}$ & $1.084$\\
\hline
\textbf{Total} & $\bm{62.252}$\\
\hline
\end{tabular}
\source{Tabela do artigo \citeonline{martins2020concra}.}
\label{table:divisao-amostras}
\end{table}
Para avaliar a dificuldade do problema, analisamos a quantidade de palavras presentes nas questões e trechos de código-fonte das amostras utilizadas no treinamento e avaliação dos nossos modelos. De acordo com a Tabela~\ref{table:statistical-descriptive-of-pythons-sample}, as questões tem aproximadamente em média $9$ palavras com um desvio padrão de $\pm 3.71$. Já os trechos de código apresentam aproximadamente uma média de $44$ e um desvio padrão de $\pm 49$. O valor alto do desvio padrão para os trechos de código é um indicativo de maior variabilidade da quantidade de palavras. Estes valores da média e desvio padrão alto podem ser influenciados por pontos fora da curva também, i.e., trechos de código com uma enorme quantidade de palavras. Para entender um pouco melhor como está distribuída esta quantidade, apresentamos os percentis na Tabela~\ref{table:statistical-descriptive-of-pythons-sample} e o diagrama de caixa com os respectivos quartis na Figura~\ref{fig:boxplot-number-of-words}.
\begin{table}[h]
\centering
\caption[Estatística descritiva da quantidade de palavras nas amostras de questões e trechos de código em Python utilizados no treinamento e avaliação dos modelos.]{Estatística descritiva da quantidade de palavras nas amostras de questões e trechos de código em Python utilizados no treinamento e avaliação dos modelos. O prefixo $\mathbf{N_{2}}$ refere-se a amostra de treinamento, conforme a Figura~\ref{fig:distinct-subset-python-pair-question-code}. Os prefixos \textbf{Dev} e \textbf{Eval} referem-se a amostra para escolha do modelo e a amostra para avaliação final conforme procedimento ilustrado nas Figuras \ref{fig:evaluation-process} e \ref{fig:final-evaluation-process}. Os sufixos \textbf{Q} e \textbf{C} referem-se ao conjunto de palavras das questões e trechos de código-fonte, respectivamente. Essa tabela inclui a média, desvio padrão e os percentis 25\%, 50\% e 75\% após o pré-processamento. Os detalhes sobre o pré-processamento estão presentes na Seção~\ref{sec:pre-processamento}.}
\begin{tabular}{ p{5em} P{4em} P{4em} P{4em} P{4em} P{4em} P{4em} }
\hline
& \multicolumn{5}{c}{\textbf{Quantidade de palavras}}\\
\hline
& \textbf{$\mathbf{N_{2}}$\_Q} & \textbf{$\mathbf{N_{2}}$\_C} & \textbf{Dev\_Q} & \textbf{Dev\_C} & \textbf{Eval\_Q} & \textbf{Eval\_C} \\
\hline
Média & $8.89$ & $44.41$ & $9.15$ & $44.31$ & $9.35$ & $42.78$ \\
Desvio padrão & $3.65$ & $55.19$ & $3.64$ & $51.87$ & $3.85$ & $43.82$ \\
Mínimo & $2$ & $1$ & $3$ & $1$ & $3$ & $2$ \\
25\% & $6$ & $16$ & $7$ & $15$ & $6$ & $15$ \\
50\% & $8$ & $30$ & $9$ & $29$ & $9$ & $29$ \\
75\% & $11$ & $54$ & $11$ & $52$ & $11$ & $55$ \\
Máximo & $32$ & $3566$ & $25$ & $708$ & $27$ & $439$ \\
\hline
\end{tabular}
\source{Elaborado pelo autor.}
\label{table:statistical-descriptive-of-pythons-sample}
\end{table}
Tanto pela Tabela~\ref{table:statistical-descriptive-of-pythons-sample} quanto pela Figura~\ref{fig:boxplot-number-of-words}, podemos verificar que as questões apresentam um variabilidade e quantidade menor de palavras. Aproximadamente 50\% das questões tem em torno de $6$ a $11$ palavras. Enquanto os trechos de código apresentam 50\% das vezes a quantidade aproximada de $15$ a $54$ palavras. A partir destas informações, podemos dizer que as nossas amostras são constituídas por questões relativamente curtas e trechos de código que variam de 1 (uma) a 3 (três) sentenças de tamanho em linguagem natural \cite{casi-newell-sentence-length-2018}. Essa amostra não apresentou dificuldades à aprendizagem por parte das redes convolucionais, pois as redes convolucionais priorizam as interações locais e tem dificuldade em correlacionar palavras muito distantes em uma sentença, o que não é o nosso caso (ver Capítulo~\ref{cap:resultados}).
\begin{figure}[h]
\centering
\caption[Diagrama de caixa para visualizar graficamente a distribuição da quantidade de palavras das questões e trechos de código-fonte por meio de quartis.]{Diagrama de caixa para visualizar graficamente a distribuição da quantidade de palavras das questões e trechos de código-fonte por meio de quartis. Este diagrama de caixa exibe o Q1 (primeiro quartil), Q2 (segundo quartil) e Q3 (terceiro quartil). Eles referem-se aos percentis 25\%, 50\% e 75\% da Tabela~\ref{table:statistical-descriptive-of-pythons-sample}. O segundo quartil (Q2) refere-se a mediana. }
\includegraphics[width=1\textwidth]{figuras/cap-experimento/boxplot_number_of_words.png}
\source{Elaborado pelo autor.}
\label{fig:boxplot-number-of-words}
\end{figure}
\subsection{Pré-processamento}
\label{sec:pre-processamento}
O pré-processamento é a primeira etapa a ser realizada após a coleta dos dados e o objetivo é transformar os dados para atender aos requisitos de entrada do modelo e facilitar a aprendizagem durante o treinamento. Em nosso trabalho, utilizamos dois procedimentos com o intuito de facilitar a aprendizagem das redes neurais:
\begin{itemize}
\item Substituição dos literais numéricos e textuais dos trechos de código-fonte
\item Representação das palavras através de um vetor de representação distribuída
\end{itemize}
Para diminuir a dispersão dos dados, substituimos os literais númericos e textuais por palavras \emph{''NUMBER''} e \emph{''STRING''}, e também as variáveis pela palavra \emph{''VAR''}. Para isto, utilizamos uma função disponibilizada por \citeonline{yao-2018} que percorre a árvore sintática do trecho do código-fonte em Python e realiza estas substituições. Além disso, utilizamos a função de \textit{tokenização} da biblioteca \Gls{keras} para transformar as questões e trechos de código-fonte em vetores numéricos, remover os caracteres especiais e tornar todas as palavras minúsculas. Os caracteres especiais removidos podem ser visualizados no código a seguir, através do parâmetro \textit{filters} no construtor da classe \textit{Tokenizer}.
\begin{mypythonembedding}{Construtor da classe \textit{Tokenizer} da biblioteca \textit{Keras}.}
Tokenizer(filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n', lower=True, split=' ')
\end{mypythonembedding}
Para auxiliar a aprendizagem das redes neurais, as palavras que compõem as questões e os trechos de código-fonte foram representados por um vetor contínuo. Segundo \citeonline{Goodfellow-et-al-2016}, os vetores contínuos induzem a um rico espaço de similaridade, no qual conceitos semanticamente similares estão próximos, facilitando a generalização e aprendizagem das redes neurais. Para mapear as palavras para vetores de representação distribuída, utilizamos o algoritmo \textit{word2vec} com \textit{skip-gram}. Segundo \citeonline{mikolov2013distributed}, \textit{skip-gram} obteve um bom desempenho em problemas semânticos, e.g., relacionar nomes de cidades a país, aproximar uma palavra masculina à sua palavra correspondente feminina. Essa é uma característica importante, pois irá auxiliar as redes neurais a diferenciarem instruções de decisão (\textit{if, else, elif}) de instruções de repetição (for, while), por exemplo.
Conforme apontado no Capítulo~\ref{cap:abordagem}, mapeamos cada palavra presente nas questões e trechos de código-fonte para uma matriz única $\bm{T_{v}}^{|V| X 100}$, onde $|V|$ é o tamanho do vocabulário formado pelas palavras presentes nas questões e trechos de código-fonte e $100$ é a dimensão do vetor. Para ilustrar o conceito de vetor contínuo e este rico espaço de similaridade, criamos a Figura~\ref{fig:tsne-code-snippet-python} utilizando a ferramenta t-SNE, que permite visualizar dados de dimensão elevada \cite{scikit-learn-tsne-2019, quora-tsne-2019}. Nessa figura exibimos os vetores de representação distribuída das 66 palavras mais frequentes do vocabulário $\bm{V}$ e pode-se notar as similaridades entre as palavras \emph{file}, \emph{open} e \emph{write}, assim como \emph{set}, \emph{dict} e \emph{list}, devido a proximidade entre elas.
\begin{figure}[H]
\caption[Representação em 2D dos vetores de representação distribuída das 66 palavras mais frequentes do vocabulário $\bm{V}$.]{Representação em 2D dos vetores de representação distribuída das 66 palavras mais frequentes do vocabulário $\bm{V}$. O vocabulário $\bm{V}$ é composto por palavras presentes nas questões e trechos de código-fonte em Python das amostras $N_{2}$ e $N_{3}$. Imagem gerada através da ferramenta t-SNE que permite a visualização em 2D de dados de dimensão elevada. Vetores de representação distribuída criados a partir do \textit{word2vec} com o algoritmo \textit{skip-gram} e o parâmetro \textit{window} com o valor $5$. Figura do artigo \citeonline{martins2020concra}.}
\includegraphics[width=1\textwidth]{figuras/cap-experimento/code_tsne.png}
\source{Elaborado pelo autor.}
\label{fig:tsne-code-snippet-python}
\end{figure}
\section{Treinamento}
\label{sec:treinamento}
Após as etapas de coleta de dados e pré-processamento, a próxima etapa é a de treinamento. Durante a etapa de treinamento, as redes neurais mapeiam o conjunto de entradas através das suas várias camadas \textit{hidden} para vetores contínuos em um espaço dimensional reduzido. A função objetivo calcula a diferença entre a saída do modelo e a saída desejada. No nosso caso, a função objetivo é a função de perda de articulação (ver Seção~\ref{sec:funcao-objetivo}). Conforme citado na Seção~\ref{sec:funcao-objetivo}, o objetivo dessa função é induzir o nosso modelo a classificar os trechos de código-fonte anotados como corretos com uma pontuação maior que os trechos de código-fonte incorretos. A diferença entre a saída do modelo e a saída desejada é propagada de volta (\textit{backpropagation}) através da rede para ajustar os pesos de suas conexões, ajustando a rede neural incrementalmente a fim de obter um melhor desempenho na tarefa definida pela função objetivo dentro dos limites estipulados pelos dados de treinamento (zona de interpolação) \cite{hasson-direct-fit-to-nature-evolutionary-perspective-ann:2020}. Para realizar a etapa de treinamento, algumas decisões foram tomadas. Listamos a seguir os principais pontos pelos quais tomamos decisão:
\begin{itemize}
\item Divisão da amostra de treinamento e validação
\item Duração do treinamento e critério de parada
\item Critério de seleção do modelo
\item Função objetivo
\end{itemize}
Conforme apontado pela Tabela~\ref{table:training-decisions}, dividimos a nossa amostra durante o treinamento em 70\% / 30\%, 70\% é utilizada para treinamento e 30\% para a validação, mesma divisão adotada por \citeonline{yao-2018} e \citeonline{cambronero-deep-learning-code-search:2019}. Adotamos os mesmos critérios de \citeonline{iyer-etal-2016-summarizing} e \citeonline{yao-2018} para seleção do modelo, duração do treinamento e critério de parada. Com relação à função objetivo, utilizamos a função de perda de articulação comumente utilizada na área de recuperação de informação e classificação de documentos e que foi utilizada também por \citeonline{Gu-deep-code-search:2018} e \citeonline{cambronero-deep-learning-code-search:2019}.
\begin{table}[h]
\centering
\caption{Sumário dos principais itens pelos quais tomamos decisão durante a etapa de treinamento.}
\begin{tabular}{ | p{5cm} | p{10cm} | }
\hline
\textbf{Item} & \textbf{Decisão} \\
\hline
Divisão da amostra de treinamento e validação & Utilizamos o conjunto $N_{2}$ composto por $60.083$ pares de questões e trechos de código-fonte. Durante o treinamento $42.058$ foram utilizadas para o conjunto de treinamento e $18.025$ para o conjunto de validação.\\
\hline
Duração do treinamento e critério de parada & O modelo é treinado durante 500 épocas. Caso o valor da função de perda de articulação na amostra de treinamento fique abaixo de $0,0001$ ou o valor da função de perda na amostra de validação não diminua após 25 épocas consecutivas, o treinamento é interrompido.\\
\hline
Critério de seleção do modelo & A cada época o modelo é avaliado em uma amostra anotada manualmente. O modelo que obtitver a maior média MRR é selecionado. Mais detalhes sobre o procedimento de critério de seleção pode ser visualizado na Seção~\ref{sec:criterio-selecao-modelo-treinamento}.\\
\hline
Função objetivo & Adotamos a função de perda de articulação como função objetivo do nosso modelo (ver Seção~\ref{sec:funcao-objetivo}). \\
\hline
\end{tabular}
\source{Elaborado pelo autor.}
\label{table:training-decisions}
\end{table}
\subsection{Critério de seleção do modelo}
\label{sec:criterio-selecao-modelo-treinamento}
Conforme citado anteriormente, adotamos o mesmo critério de seleção do modelo de \citeonline{iyer-etal-2016-summarizing} e \citeonline{yao-2018}. O modelo é treinado durante 500 épocas e a cada época o modelo é avaliado na amostra \emph{DEV}. O intuito desta avaliação é obter o melhor modelo conforme a métrica \acrshort{mrr}. Um outro critério de seleção do modelo comumente utilizado é selecionar o modelo com o menor erro na amostra de validação. Porém, conforme os resultados apontados pelos nossos experimentos (ver Seção~\ref{sec:ape-criterio-de-selecao-do-modelo-experimento} no Apêndice), não houve diferença significativa no desempenho dos modelos com relação ao critério de seleção. Ao final, optamos por manter o mesmo critério de seleção adotados por \citeonline{iyer-etal-2016-summarizing} e \citeonline{yao-2018}. A ilustração deste procedimento de critério de seleção pode ser visualizado na Figura~\ref{fig:evaluation-process}.
\begin{figure}[p]
\centering
\caption[Ilustração do exemplo de treinamento de uma rede convolucional.]{Ilustração do exemplo de treinamento de uma rede convolucional. Este procedimento é o mesmo adotado por \citeonline{iyer-etal-2016-summarizing}. $E$ é uma variável de controle para armazenar a quantidade de épocas. $P$ refere-se a quantidade de iterações consecutivas em que não há diminuição do resultado da função de perda na amostra de validação. Os vetores de entrada dos modelos durante o treinamento são os vetores de representação distribuída $q, c^{+}, c^{'} \in \mathbb{R}^{150 X d}$, onde $150$ é o tamanho fixo dos vetores e $d$ é a dimensão do vetor. $F \in \mathbb{R}^{k X d}$ é o filtro convolucional, onde $k$ é o tamanho da janela e $d$ a dimensão do vetor. $h_{\theta}$ é a função \textit{cosine} de similaridade, $J$ é a função de perda \textit{hinge} e $m$ é a margem. O procedimento de treinamento está descrito na Seção~\ref{sec:treinamento}.}
\includegraphics[height=1.3\textwidth]{figuras/cap-experimento/evaluation_process.pdf}
\source{Elaborado pelo autor.}
\label{fig:evaluation-process}
\end{figure}
De acordo com a Figura~\ref{fig:evaluation-process}, a cada época um modelo candidato é avaliado quanto a média MRR. Para calcular a média MRR, calculamos inicialmente a distância de similaridade entre cada questão da amostra DEV e 50 trechos de código-fonte, onde um trecho de código-fonte é a resposta anotada como correta e 49 são trechos de código distratores selecionados aleatoriamente da amostra de treinamento. Posteriormente, os trechos de código-fonte são ordenados de forma decrescente, do mais similar (maior pontuação) ao menos similar (menor pontuação). Com os trechos ordenados, verificamos a posição do trecho anotado como correto para cálculo do \textit{reciprocal rank}, pois \textit{reciprocal rank} é o inverso da primeira posição encontrada para o trecho anotado como correto. Com o \textit{reciprocal rank}, podemos, finalmente, calcular o \acrshort{mrr}, que é a média do \textit{reciprocal rank} para a amostra inteira \cite{Gu-deep-code-search:2018}:
\begin{equation}\label{eq:mrr}
MRR = \frac{1}{n} * \sum_{i = 1}^{n}\frac{1}{p_{i}^{+}}
\end{equation}
Onde $n$ é a quantidade de questões presentes na amostra, $p_{i}^{+}$ é a posição da primeira ocorrência do trecho anotado como correto entre os trechos ordenados. Este procedimento é repetido durante 20 iterações. A cada iteração, outros 49 distratores são selecionados e, ao final, obtém-se a média \emph{MRR} do modelo. Basicamente, a cada época verificamos o resultado MRR do modelo comparando a resposta anotada como correta com outros 49 distratores. Selecionamos o modelo com a melhor média MRR. Este modelo escolhido foi utilizado na avaliação final.
\section{Análise do desempenho da rede neural}
\label{sec:analise-do-desempenho-da-rede-neural}
Para realizar a análise de desempenho da rede neural, três itens devem ser levados em consideração:
\begin{itemize}
\item Amostra de avaliação
\item Métrica de avaliação
\item Modelos de referência para comparação
\end{itemize}
Avaliamos o nosso modelo utilizando a amostra EVAL, que contém 1084 pares de questões e trechos de código-fonte anotados manualmente. Utilizamos o mesmo procedimento de avaliação proposto por \citeonline{iyer-etal-2016-summarizing} e \citeonline{yao-2018} juntamente com a métrica MRR. A métrica MRR é uma medida para sabermos se o modelo está conseguindo classificar o item relevante entre as primeiras posições de uma lista ordenada de itens.
No nosso trabalho, o item relevante é um trecho de código-fonte anotado como correto e a lista ordenada de itens é composta pelo trecho de código anotado como correto e outros 49 trechos de código-fonte distratores.
Pelo fato de termos apenas um trecho de código-fonte anotado, a métrica MRR torna-se uma medida apropriada para avaliar o nosso modelo, pois o cálculo do MRR leva apenas um item relevante em consideração, desconsiderado todos outros itens da lista, conforme Equação~\ref{eq:mrr}. Outras métricas como \acrshort{map} e \acrshort{ndcg}, comumente utilizadas na avaliação de modelos na recuperação de informação \cite{kun-learning-to-rank:2018}, levam em consideração a relevância dos outros itens da lista, o que demandaria, no nosso caso, um trabalho de anotar e indicar quais trechos de código são relevantes a uma dada questão. Como a anotação e indicação da relevância dos trechos de código-fonte estão fora do escopo do presente trabalho, optamos por utilizar a mesma métrica MRR adotada por \citeonline{iyer-etal-2016-summarizing} e \citeonline{yao-2018}.
Além da métrica de avaliação, é necessário comparar o nosso modelo com outras arquiteturas de referência para termos uma base de comparação. No nosso caso, comparamos o desempenho das redes convolucionais com outras duas arquiteturas: A arquitetura \textit{Embedding} e arquitetura \textit{Unif} proposta por \citeonline{cambronero-deep-learning-code-search:2019}. A arquitetura \textit{Embedding} foi utilizada como base de comparação, pois é uma arquitetura mais simples, que consiste de uma camada de \gls{bag-of-words} seguida de uma camada \textit{maxpool}. A arquitetura \textit{Unif} utiliza um mecanismo de atenção para obter um vetor contínuo dos trechos de código-fonte e atualmente é o estado da arte na recuperação de trechos de código-fonte.
\subsection{Avaliação}
\label{sec:avaliacao}
Utilizamos o mesmo procedimento descrito na Seção~\ref{sec:criterio-selecao-modelo-treinamento} para avaliação final dos nossos modelos. Porém, a avaliação dos modelos com relação à métrica MRR foi feita na amostra EVAL. Calculamos o MRR para cada par $<q_{i}, c_{i}^{+}>$ da amostra \emph{EVAL} juntamente com outros 49 distratores $c'$ que são selecionados aleatoriamente da amostra de treinamento, tal que $c_{i}^{+} \neq c'$. Este procedimento de cálculo do MRR é repetido durante 20 iterações e ao final obtemos o resultado do nosso modelo na avaliação. A ilustração desse procedimento pode ser visualizada na Figura~\ref{fig:final-evaluation-process}.
\begin{figure}[h]
\centering
\caption[Ilustração do procedimento de avaliação proposto por \citeonline{iyer-etal-2016-summarizing}.]{Ilustração do procedimento de avaliação proposto por \citeonline{iyer-etal-2016-summarizing}. O modelo com a maior média MRR no treinamento é avaliado na amostra EVAL. Durante 20 iterações, calculamos o MRR para os pares $<q, c^{+}>$ junto com outros 49 $c^{'}$ distratores, que são escolhidos da amostra de treinamento. Onde $q, c^{+}, c^{'} \in \mathbb{R}^{150 X d}$, tal $c^{+} \neq c^{'}$. A cada iteração outros 49 distratores $c^{'}$ são escolhidos. E ao final, obtemos o resultado do modelo.}
\includegraphics[height=1\textwidth]{figuras/cap-experimento/final_evaluation_process.pdf}
\source{Elaborado pelo autor.}
\label{fig:final-evaluation-process}
\end{figure}
\subsection{Arquiteturas}
Para este estudo, comparamos a arquitetura CNN com outras duas arquiteturas: \textit{Unif} e \textit{Embedding}. A arquitetura \textit{Unif} proposta por \citeonline{cambronero-deep-learning-code-search:2019} utiliza duas abordagens diferentes para representação das questões e trechos de código-fonte. No caso das questões, os vetores contínuos de cada palavra foram combinados através de uma média aritmética simples. Quanto aos trechos de código, aplicou-se um mecanismo de auto-atenção (\textit{self-attention}) global, proposto por \citeonline{luong-etal-2015-effective}, que calcula uma média ponderada sobre os vetores contínuos e aprende a ``prestar atenção`` nas palavras mais importantes. \citeonline{cambronero-deep-learning-code-search:2019} optaram pelo mecanismo de auto-atenção para simplificar a arquitetura e, com isso, não há a necessidade de utilizar uma rede neural recorrente, por exemplo, para alinhar os vetores contínuos das questões às perguntas no cálculo do vetor de atenção.
Já a arquitetura \textit{Embedding} combina os vetores de representação distribuída de cada palavra através de uma camada \textit{maxpool}. A camada \textit{maxpool} reduz a dimensionalidade do vetor selecionando apenas os maiores elementos. Essa arquitetura serviu de base de comparação, pois ela seleciona somente os maiores elementos do vetor, diferentemente das outras arquiteturas que ''aprendem'' a extrair as características mais importantes. As figuras~\ref{fig:cnn-architecture}, \ref{fig:unif-architecture} e \ref{fig:embedding-architecture} ilustram as arquiteturas CNN, \textit{Unif} e \textit{Embedding}, respectivamente.
\begin{figure}[h]
\centering
\caption[Figura da arquitetura CNN descrita no Capítulo~\ref{cap:abordagem}.]{Figura da arquitetura CNN descrita no Capítulo~\ref{cap:abordagem}. O termo \emph{Embedding Layer} refere-se a camada composta por vetores de representação distribuída. OL é o acrônimo de \emph{Output Layer}, i.e., camada de saída. $O_{q}$ e $O_{c}$ indicam a saída do vetor de representação final das questões e trechos de código-fonte, respectivamente. \emph{Cosine} indica a função de similaridade.}
\includegraphics[width=0.8\textwidth]{figuras/cap-experimento/cnn-architecture-proposal.pdf}
\source{Elaborado pelo autor.}
\label{fig:cnn-architecture}
\end{figure}
\begin{figure}[h]
\centering
\caption[Figura da arquitetura \Gls{unif} proposta por \citeonline{cambronero-deep-learning-code-search:2019}.]{Figura da arquitetura \Gls{unif} proposta por \citeonline{cambronero-deep-learning-code-search:2019}. \emph{Embedding} refere-se a camada composta pelos vetores de representação distribuída. \emph{Média} é camada que combina os vetores através de uma média aritmética simples. \emph{Atenção} refere-se ao \gls{mecanismo-atencao}, que combina os vetores através de uma média ponderada e aprende a ''prestar atenção'' nos termos mais relevantes. \emph{OL} é o acrônimo de \emph{Output Layer}, i.e., camada de saída. $O_{q}$ e $O_{c}$ referem-se ao vetor de representação final das questões e trechos de código, respectivamente. \emph{Cosine} indica a função de similaridade. }
\includegraphics[width=0.8\textwidth]{figuras/cap-experimento/unif-architecture.pdf}
\source{Elaborado pelo autor.}
\label{fig:unif-architecture}
\end{figure}
\begin{figure}[h]
\centering
\caption[Figura da arquitetura de referência \textit{Embedding} para comparação.]{Figura da arquitetura de referência \textit{Embedding} para comparação. \emph{Embedding Layer} refere-se a camada composta pelos vetores de representação distribuída. \emph{OL} é o acrônimo de \emph{Output Layer}, i.e., camada de saída. $O_{q}$ e $O_{c}$ referem-se aos vetores de representação final das questões e trechos de código, respectivamente. \emph{Cosine} refere-se a função de similaridade. }
\includegraphics[width=0.8\textwidth]{figuras/cap-experimento/embedding-architecture.pdf}
\source{Figura do artigo \citeonline{marcelo-vem-2019}.}
\label{fig:embedding-architecture}
\end{figure}
Nas figuras \ref{fig:cnn-architecture}, \ref{fig:unif-architecture} e \ref{fig:embedding-architecture}, a palavra \textit{Embedding Layer} refere-se ao vetor contínuo das palavras. \emph{OL} é um acrônimo de \textit{Output Layer} ou camada de saída, $O_{q}$ indica a camada de saída da questão e $O_{c}$ indica a camada de saída do trecho de código-fonte. As arquiteturas CNN e \textit{Embedding} utilizam uma camada \textit{maxpool} e todas as arquiteturas, ao final, calculam a similaridade através da função \textit{cosine}. Os ajustes dos hiper-parâmetros são discutidos com mais detalhes no Apêndicê~\ref{ape:ajuste-hiper-parametros-cnn}.