Relatório integrado com rede neural, árvore de decisão e máquina de vetores de suporte.
Autores
Erlon Lacerda
Maria Bandeira
Data de Publicação
24 de outubro de 2024
O relatório final preserva a execução apresentada na disciplina e compara três famílias de classificadores sobre o alvo binário preparado nas etapas anteriores.
Introdução
O nosso objetivo para escolher o dataset era avaliar as notas e desempenho dos alunos com base em variáveis sociais / de estudo, sem considerar questões socioeconômicas. Sobre aplicações sociais, pensamos em como seria importante acompanhar o aluno durante o tempo anterior à prova, fazendo questionários antes, ir trabalhando nos pontos fracos do aluno e melhorar o desempenho geral do ensino nas escolas municipais com base nas respostas dadas pelo aluno. Esse estudo promove a ideia de que o desempenho do aluno poderia ser “previsto”, mesmo se o questionário fosse respondido meses antes da prova em si, o que trás um alarme para como a qualidade de ensino deve ser melhorada, mas o aluno também pode ter melhores resultados, caso tenha um acompanhamento considerando essas variáveis.
Descrição do Dataset
O Sistema de Avaliação da Educação Básica (Saeb) é um conjunto de avaliações externas em larga escala que permite ao Inep realizar um diagnóstico da educação básica brasileira e de fatores que podem interferir no desempenho do estudante. Desde 1995, a avaliação bienal busca fornecer um panorama da Educação Básica e sofreu algumas mudanças metodológicas para aprimoramento. Juntamente com outros indicadores, as notas do SAEB estruturam a nota do Índice de Desenvolvimento da Educação Básica (Ideb). Por meio de testes e questionários, aplicados a cada dois anos na rede pública e em uma amostra da rede privada, o Saeb reflete os níveis de aprendizagem demonstrados pelos estudantes avaliados, explicando esses resultados a partir de uma série de informações contextuais. Avalia as séries: 5º ano, 9º ano e 3º ano do EM. Em 2019, tem o 2º ano do E.F.
Tratamento / Análise de Dados
Após a escolha do Dataset, disponível em Base dos Dados - SAEB, precisamos fazer o tratamento dos dados, tratamento das colunas, análise das correlações / feature engineering e análise dos dados, tudo para que tivéssemos os dados da melhor forma possível. Os notebooks estão separados em ordem, visando a Reproducible Research. Considerados como códigos-fonte a parte do projeto principal, eles estão separados na pasta treatment_analysis/ da forma:
initial-treatment.ipynb: Contém o tratamento inicial, a lógica para juntar os dados de 2007 até 2017 (considerando que as provas são realizadas a cada 2 anos) e tratamentos básicos como preencher idades faltantes e remover questionários nao preenchidos;
columns-treatment.ipynb: Contém a transformação das colunas do dataset (com base no dicionário das perguntas) pra valores discretos, de forma ordinal. Também são retiradas colunas não consideradas para o estudo e remoção de outliers com base na proficiencia / erro_padrao;
correlation_featureengineering.ipynb: Contém a análise das correlações entre as variáveis / features a partir de heatmaps e remoção de colunas com baixa ou nenhuma correlação com o desempenho do aluno. O dataset utilizado para treinar os modelos também é salvo desse notebook;
data-analysis.ipynb: Contém a análise dos dados, com foco na distribuição das classes com base na proficiência e erro padrão do aluno e a aplicação de PCA para relacionar as variáveis entre si.
data-interpos.ipynb: Contém a análise e a remoção de interposição (ruído) nos dados, através de força bruta analisando todas as possibilidades, balanceando os grupos. Também é feita a remoção da classe ‘Proficiente’, e ao mesmo tempo a junção das classes ‘Insuficiente’ e ‘Básico’.
# colunas a serem excluidas (não serão consideradas como features em X)exclude = ['ano', 'sigla_uf', 'id_aluno', 'situacao_censo', 'disciplina', 'proficiencia', 'erro_padrao', 'proficiencia_saeb','erro_padrao_saeb', 'sexo', 'raca_cor','ano_nascimento', 'rede', 'id_escola', 'serie', 'turno', 'escola_publica', 'desempenho_aluno']exclude = [col for col in df.columns if col in exclude]
Mostrar código
# print unique values for each columnfor col in df.columns:if col notin exclude:print(col, df[col].unique())
Identificar o número de exemplos (\(N\)) e número de features;
Mostrar código
N =len(X) # número de exemplosn_features = X.shape[1] # número de featuresprint('Número de exemplos:', N)print('Número de features:', n_features)
Número de exemplos: 1173
Número de features: 13
Separar os dados (𝑋,𝑦) entre treinamento e teste, justificando a quantidade de exemplos de cada grupo de dados. Não mexer no teste até a fase final de computação das métricas de aprendizado;
Mostrar código
# separando em 20% para teste e 80% para treino# por ser o valor mais comumX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# agora, separando os dados de treino em 20% para validaçãoX_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42)print('Número de exemplos de treino:', len(X_train))print('Número de exemplos de validação:', len(X_val))print('Número de exemplos de teste:', len(X_test))
Número de exemplos de treino: 750
Número de exemplos de validação: 188
Número de exemplos de teste: 235
Aplicar normalização ou padronização dos dados, justificando a escolha do método.
Mostrar código
# não precisaremos aplicar normalização ou padronização nos dados,# pois são de origem binária para todas as colunas, isto é, vão de 0 a 1.# esse tratamento foi feito na etapa de feature engineering
2. Implementação do modelo baseado em Rede Neural
Definir a arquitetura da rede neural (número de camadas e o número de neurônios por camada) a partir do número de exemplos disponíveis no dataset e da “Regra de Ouro”, que é consequência da Teoria da Generalização do Aprendizado. Use o cálculo da dimensão VC para justificar a escolha da arquitetura mencionada. Obs: Não esqueça da existência do bias em cada neurônio e use o Teorema da Aproximação Universal;
# usando uma camada ocultaprint(X_train.shape)print('De acordo com a regra de ouro da dimensão VC, \no número de neurônios na camada única oculta da Rede Neural \ndeve ser menor ou igual que')print('----> '+str(num_neurons(N, n_features)))
(750, 13)
De acordo com a regra de ouro da dimensão VC,
o número de neurônios na camada única oculta da Rede Neural
deve ser menor ou igual que
----> 7
Vamos inicializar um modelo sequencial com as escolhas de hiperparâmetros: - função de ativação ‘ReLU’: É uma função com custo computacional mais baixo do que ‘tanh’, o que acaba deixando o treinamento mais rápido. - Dropout: Visando não deixar o modelo com baixa capacidade de aprendizado, foi adicionado mais parâmetros do que o recomendado (mín. 75), e com isso foi necessário adicionar Dropout para regularizar e prevenir overfitting. - Função de ativação ‘sigmoid’: É a função de ativação utilizada para classificação binária. - Função de perda ‘binary_crossentropy’: É a função de perda utilizada para classificação binária.
Mostrar código
model = Sequential()model.add(Dense(7, input_dim=X_train.shape[1], activation='relu', kernel_regularizer='l2'))model.add(Dense(1, kernel_initializer='normal', activation='sigmoid'))# não precisamos utilizar early stopping o modelo é de baixa complexidade#es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=10)# Compilar o modelomodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])model.summary()
Computar o \(𝐸_{in}\) e \(𝐸_{out}\) para analisar a existência de overfitting;
Mostrar código
Ein = history.history['loss'][-1]Eout = history.history['val_loss'][-1]print(f'\n--> E_in: {Ein:.4f}')print(f'--> E_out: {Eout:.4f}')print('--> E_in - E_out:', Ein - Eout)# como podemos ver não há overfitting devido a pequena diferença entre E_in e E_out
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier(random_state=42)
Plotar a árvore e computar \(𝐸_{in}\) e \(𝐸_{out}\) para analisar a existência de overfitting;
Nesse caso, podemos observar que houve overfitting, visto que a diferença entre o Ein e o Eout é significativa
Regularizar o valor de 𝛼 utilizando o algoritmo de Minimal Cost-Complexity já implementado na classe DecisionTreeClassifier, para encontrar a árvore que minimize a relação:
\[𝑃𝑢𝑟𝑒𝑧𝑎(𝑇)+ 𝛼 ∙\#𝑓𝑜𝑙ℎ𝑎𝑠(𝑇).\]
Esse processo deve ser realizado com a técnica de cross validation, onde o tamanho do fold deve ser definido pela dimensão do conjunto de treino;
Best Alpha: 0.0010465035937320306, Best Score: 0.8883251500272777
O valor de folds foi escolhido levando em consideração o tamanho dos dados de treino, que pode ser considerado relativamente grande. Nesse caso, é recomendado o uso de 5 a 10 folds. Além disso, considerou-se o custo computacional, quanto mais folds, maior o custo, logo optamos por utilizar 5 folds.
Plotar a imagem e computar as métricas de qualidade da melhor árvore construída.
Construir um modelo de SVM, regularizando dos parâmetros 𝐶 e 𝛾 (gama) através de cross validation, onde o tamanho do fold deve ser definido pela dimensão do conjunto de treino;
Fitting 5 folds for each of 240 candidates, totalling 1200 fits
CPU times: user 18.3 s, sys: 1.33 ms, total: 18.3 s
Wall time: 18.3 s
{'C': 500, 'gamma': 0.05, 'kernel': 'poly'}
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
SVC(C=500, gamma=0.05, kernel='poly')
Computar o valor de \(𝐸_{in}\), \(𝐸_{out}\) e o valor de \(𝐸_{out}\) esperado, baseado no número de vetores de suporte utilizados na solução, para analisar a existência de overfitting;
Mostrar código
print(X_train.shape)# computando Ein, Eout e o valor de Eout esperadoy_pred_train = clf.predict(X_train)Ein =1- accuracy_score(y_train, y_pred_train)Eout =1- accuracy_score(y_test, y_pred)print(f'Ein: {Ein:.2f}')print(f'Eout: {Eout:.2f}')print()# calculando Eout esperadon_support_vectors =len(clf.support_)n_samples =len(X_train)print(f'Número de vetores de suporte: {n_support_vectors}')Eout_expected = Ein + (n_support_vectors / n_samples)print(f'Eout esperado: {Eout_expected:.2f}')
(938, 13)
Ein: 0.05
Eout: 0.13
Número de vetores de suporte: 257
Eout esperado: 0.33
Computar as métricas de qualidade de classificação do melhor modelo encontrado