Esse é o quarto post de uma série de como construir um produto data-driven de ponta a ponta, caso você ainda não tenha acompanhado os demais, abaixo segue uma síntese com os respectivos links 😀.

  1. Em metadados de normas jurídicas federais coletamos dados do sistema LexML.
  2. Em um produto orientado a dados governamentais: parte 1 realizamos uma análise exploratória dos dados e definimos um recorte e um escopo para os dados do projeto.
  3. Em um produto orientado a dados governamentais: parte 2 realizamos a definição dos dos datasets de treino, validação e teste

No último post segmentamos nossos dados em conjunto de treinamento, validação e teste que serão utilizados para diferentes finalidades ao longo do processo de treinamento do nosso modelo de aprendizado de máquina.

xTrain = pd.read_csv("./data/xTrain.csv", sep=';').set_index('legislationIdentifier')
yTrain = pd.read_csv("./data/yTrain.csv", sep=';').set_index('legislationIdentifier')

null_description = xTrain[xTrain['description'].isnull()].index
xTrain = xTrain.drop(null_description)
yTrain = yTrain.drop(null_description)

xValidation = pd.read_csv("./data/xValidate.csv", sep=';').set_index('legislationIdentifier')
yValidation = pd.read_csv("./data/yValidate.csv", sep=';').set_index('legislationIdentifier')

null_description = xValidation[xValidation['description'].isnull()].index
xValidation = xValidation.drop(null_description)
yValidation = yValidation.drop(null_description)

yTrain['keywords'] = yTrain['keywords'].apply(transf_str_into_list)
yValidation['keywords'] = yValidation['keywords'].apply(transf_str_into_list)

Vamos iniciar nosso processo de treinamento, instanciando o TfidfVectorizer que irá construir uma DTM e depois matrix TF-IDF das ementas das normas jurídicas que estamos trabalhando.

vect = TfidfVectorizer(
    strip_accents='unicode',
    analyzer='word',
    tokenizer=word_tokenize,
    max_features=5000)
multilabel = MultiLabelBinarizer()

Aplicamos as transformações nos dados de treinamento e validação.

yValidation_transformed = multilabel.fit_transform(yValidation['keywords'])
xValidation_transformed = vect.fit_transform(xValidation['description'])
%%time
yTrain_transformed = multilabel.fit_transform(yTrain['keywords'])
xTrain_transformed = vect.fit_transform(xTrain['description'])
Wall time: 6.03 s

Em seguida, vamos criar um baseline para nosso problema de classificação multilabel. Escolhemos a Regressão Logística como ponto de partida do nosso trabalho.

Regressão Logística (baseline)

lr = LogisticRegression()
clf = OneVsRestClassifier(lr)
clf.fit(xTrain_transformed, yTrain_transformed)
OneVsRestClassifier(estimator=LogisticRegression())
y_pred = clf.predict(xValidation_transformed)

Para o caso de classificações multilabel o hamming loss, diferentemente, da função Zero one loss não penaliza um predição caso essa não seja idêntica ao set alvo, isto é, não precisamos de um match exato, portanto, ela apenas penaliza labels de forma individual. Por exemplo, um normativo que possui as seguintes tags: [CREDITO SUPLEMENTAR, REFORÇO, ORÇAMENTO DA SEGURIDADE SOCIAL, DOTAÇÃO ORÇAMENTARIA], mas foi classificado apenas como [CREDITO SUPLEMENTAR, REFORÇO, ORÇAMENTO DA SEGURIDADE SOCIAL] resultará num hamming_loss de 0.25. Os valores de hamming loss pertencem a um intervalo de 0 a 1 e quanto menor o valor, melhor. Para maiores informações. o leitor pode consultar a documentação do sklearn.

def print_hamming_loss(y_true, y_pred, clf):
    print("Clf: ", clf.__class__.__name__)
    print("Hamming loss: {}".format(hamming_loss(y_true, y_pred)))
print_hamming_loss(yValidation_transformed, y_pred, clf)
Clf:  OneVsRestClassifier
Hamming loss: 0.10763268211658625

Apesar de nossa métrica de avaliação ter resultado num valor baixo, que é o desejável, ela ainda pode ser considerada uma variante de uma métrica de acurácia. Portanto, é sabido que em cenários onde o modelo pode predizer apenas a classe majoritária, teremos um score razoável, apesar de não ser considerado um bom modelo. Dito isso, vamos determinar em quantas ocorrências o modelo não resultou em predição nenhuma, isto é, o normativo tem N tags, mas o retorno do modelo foi ausente (0 tags preditas).

len([data for data in y_pred if ~data.any()])/y_pred.shape[0]
0.9648079306071871

O modelo apesar de um hamming loss razoável, em 96,5% das predições não retorna tag alguma. Como exemplificado abaixo:

Exemplo de resultado esperado:

multilabel.inverse_transform(np.atleast_2d(yValidation_transformed[0]))
[('AREA PRIORITARIA',
  'DECLARAÇÃO',
  'DESAPROPRIAÇÃO',
  'DESTINAÇÃO',
  'ESTADO DE MINAS GERAIS MG',
  'IMOVEL RURAL',
  'INSTITUTO NACIONAL DE COLONIZAÇÃO E REFORMA AGRARIA INCRA',
  'INTERESSE SOCIAL',
  'MUNICIPIO',
  'REFORMA AGRARIA')]

Predição realizada:

multilabel.inverse_transform(np.atleast_2d(y_pred[1]))
[()]

Podemos realizar uma inspeção mais detalhada utilizando a função classification_report. Assim, temos evidenciado que a grande maioria das tags temos tanto precision como recall nulos, que no nosso caso em específico podemos interpretar que o modelo não teve capacidade de predição para nenhuma dessas ocorrências.

mapping_tags = list(multilabel.classes_)
print(classification_report(yValidation_transformed, y_pred, target_names=mapping_tags, zero_division=0))
                                                           precision    recall  f1-score   support

                                     ACORDO INTERNACIONAL       0.00      0.00      0.00       505
                                                ALTERAÇÃO       0.00      0.00      0.00      1018
                                                   AMBITO       0.00      0.00      0.00       406
                                                APROVAÇÃO       0.00      0.00      0.00      1944
                                         AREA PRIORITARIA       0.00      0.00      0.00       783
                                                      ATO       0.00      0.00      0.00      1465
                                              AUTORIZAÇÃO       0.00      0.00      0.00       835
                                                   BRASIL       0.00      0.00      0.00       413
                                              COMPETENCIA       0.00      0.00      0.00       445
                                               COMPOSIÇÃO       0.00      0.00      0.00       310
                                                CONCESSÃO       0.00      0.00      0.00      1890
                                               CORRELAÇÃO       0.00      0.00      0.00       263
                                      CREDITO SUPLEMENTAR       0.00      0.00      0.00      1302
                                                  CRIAÇÃO       0.00      0.00      0.00       608
                                                CRITERIOS       0.00      0.00      0.00       390
                                               DECLARAÇÃO       1.00      0.00      0.00      1197
                                           DESAPROPRIAÇÃO       0.00      0.00      0.00      1023
                                               DESTINAÇÃO       0.50      0.00      0.00      1730
                                             DISPOSITIVOS       0.00      0.00      0.00       268
                                     DOTAÇÃO ORÇAMENTARIA       0.00      0.00      0.00      1153
                              EMPRESA DE TELECOMUNICAÇÕES       0.00      0.00      0.00      1749
                                ESTADO DE MINAS GERAIS MG       0.00      0.00      0.00       396
                                   ESTADO DE SÃO PAULO SP       0.00      0.00      0.00       494
                                      ESTADO DO PARANA PR       0.98      0.50      0.66       260
                           ESTADO DO RIO GRANDE DO SUL RS       0.00      0.00      0.00       223
                                                EXECUTIVO       0.00      0.00      0.00       258
                                                 EXECUÇÃO       0.00      0.00      0.00      1870
                                                  FIXAÇÃO       0.00      0.00      0.00       365
                                            FUNCIONAMENTO       0.00      0.00      0.00       305
                                                 HIPOTESE       0.00      0.00      0.00       228
                                             IMOVEL RURAL       0.00      0.00      0.00       793
INSTITUTO NACIONAL DE COLONIZAÇÃO E REFORMA AGRARIA INCRA       0.00      0.00      0.00       794
                                         INTERESSE SOCIAL       0.00      0.00      0.00       899
                                                MUNICIPIO       0.72      0.00      0.01      3258
                                                   NORMAS       0.00      0.00      0.00      1045
                                                 OBJETIVO       0.00      0.00      0.00       511
                           ORÇAMENTO DA SEGURIDADE SOCIAL       0.00      0.00      0.00       323
                                         ORÇAMENTO FISCAL       0.00      0.00      0.00       651
                                         PAIS ESTRANGEIRO       0.00      0.00      0.00       430
                                             RADIODIFUSÃO       0.00      0.00      0.00      1749
                                          REFORMA AGRARIA       0.00      0.00      0.00       793
                                                  REFORÇO       0.00      0.00      0.00      1124
                                                RENOVAÇÃO       0.00      0.00      0.00       631
                                                  SERVIÇO       0.00      0.00      0.00      1832
                                                    TEXTO       0.00      0.00      0.00       348
                                            UNIÃO FEDERAL       0.00      0.00      0.00      1224
                                        UTILIDADE PUBLICA       0.00      0.00      0.00       320

                                                micro avg       0.49      0.00      0.01     40821
                                                macro avg       0.07      0.01      0.01     40821
                                             weighted avg       0.11      0.00      0.01     40821
                                              samples avg       0.02      0.00      0.00     40821

Pré-Processamento & Engenharia de Atributos (Feature Engineering)

Pré-Processamento

No presente momento, as nossas únicas variáveis preditoras são os tokens oriundos das ementas dos normativos que são submetidos a um pipeline que inicialmente os convertem a uma matrix DTM e por conseguinte os transformam em uma representação tf-idf. Para esse conjunto de operações a biblioteca sklearn fornece a classe TfidfVectorizer que realiza esse conjunto de operações.

Para que seja possível fazer uma inspeção das nossas variáveis preditoras, iremos recorrer a uma wordcloud para ter uma dimensão da frequência de ocorrência dos diferentes tokens no corpus em análise.

def make_word_cloud(series: pd.Series, **kwargs):
    stopwords = kwargs.get('stopwords')
    corpus_normativos = " ".join(series)
    tokenize_corpus = word_tokenize(corpus_normativos)
    if stopwords:
        print(len(stopwords))
        wordcloud = WordCloud(width=1024, height=768, stopwords=stopwords, margin=0).generate(corpus_normativos)
    else:
        wordcloud = WordCloud(width=1024, height=768, margin=0).generate(corpus_normativos)
    #show
    plt.figure(figsize=[15, 9])
    plt.imshow(wordcloud, interpolation='bilinear')
    plt.axis("off")
    plt.show()
    return wordcloud
wc = make_word_cloud(xTrain['description'])

def get_top_n_words(corpus, n=None):
    vec = CountVectorizer().fit(corpus)
    bag_of_words = vec.transform(corpus)
    sum_words = bag_of_words.sum(axis=0) 
    words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
    words_freq = sorted(words_freq, key = lambda x: x[1], reverse=True)
    return words_freq[:n]
df_common_words = pd.DataFrame(get_top_n_words(xTrain['description']), columns = ['token' , 'frequencia'])

Pelo gráfico abaixo que inspecionamos os 30 tokens mais frequentes no corpus de treinamento, identificamos que muitos destes são considerados stop words, bem como há ocorrências de numerais. Dito isso, o próximo passo do nosso pipeline de pré-processamento será compostos pelas seguintas etapas:

  • Remoção de um conjunto delimitado de stopwords;
  • Excluir tokens que sejam compostos exclusivamente de dígitos;
  • Normalizar os tokens de forma a retirar acentos e caracteres especiais.
  • Correção de grafia de tokens

alt.Chart(df_common_words[:30].sort_values(by='frequencia', ascending=False)).mark_bar().encode(
    alt.X('frequencia:Q', title='Frequência'),
    alt.Y("token:N", sort='-x'),
    tooltip=[alt.Tooltip('frequencia', title='Frequência')]
    )\
    .properties(height=500)\
    .configure_mark(color=default_color_2)    

a) Remoção de um conjunto delimitado de stopwords

Vamos utilizar uma lista compiladas de stopwords listadas nesse GIST.

b) Excluir tokens que sejam compostos exclusivamente de dígitos.

def hasNumbers(inputString):
    return any(char.isdigit() for char in inputString)

c) Normalizar os tokens de forma a retirar acentos e caracteres especiais.

def removerAcentosECaracteresEspeciais(palavra):
    # Unicode normalize transforma um caracter em seu equivalente em latin.
    nfkd = unicodedata.normalize('NFKD', palavra)
    palavraSemAcento = u"".join([c for c in nfkd if not unicodedata.combining(c)])
    #retirar espaços vazios múltiplos
    palavraSemAcento = re.sub(' +', ' ', palavraSemAcento)
    # Usa expressão regular para retornar a palavra apenas com números, letras e espaço
    return re.sub('[^a-zA-Z0-9 \\\]', '', palavraSemAcento).lower()

d) Correção de grafia de tokens

Nessa etapa faremos a correção da ocorrência de alguns tokens que estão com grafia incorreta, ou duas palavras que não estão separadas por espaço, ou mesmo palavras no plural que vamos transformá-la para o singular.

def split_selected_tokens(texto: str):
    select_tokens = {
        'dedezembro' : 'de dezembro',
        'capitalsocial' : 'capital social',
        'decretosleis' : 'decretos leis',
        'nacionaldestinada' : 'nacional destinada',
        'professorequivalente' : 'professor equivalente',
        'tvsbt' : 'tv sbt',
        'orcamentariavigente' : 'orcamentaria vigente',
        'tecnicoprofissional' : 'tecnico profissional',
        'republicafederativa' : 'republica federativa', 
        'transbrasilianaconcessionaria' : 'transbrasiliana concessionaria',
        'publicoprivada' : 'publico privada',
        'procuradorgeral' : 'procurador geral',
        'auditoriafiscal' : 'auditoria fiscal',
        'decretoslei' : 'decretos lei',
        'segurancapublica' : 'seguranca publica',
        'novadutra' : 'nova dutra',
        'queespecifica' : 'que especifica',
        'agenciasreguladoras' : 'agencias reguladoras',
        'autopistalitoral' : 'autopista litoral',
        'goiasvigencia' : 'goias vigencia',
        'governadorcelso' : 'governador celso',
        'autopistaregis' :'autopista regis',
        'valecultura' : 'vale cultura',
        'programacaoorcamentaria' : 'programacao orcamentaria',
        'dopoder' : 'do poder',
        'ministeriodo' : 'ministerio do',
        'estadosmembros' : 'estados membros',
        'papelmoeda' :'papel moeda',
        'bolsaatleta' : 'bolsa atleta',
        'complementacaoeconomica' : 'complementacao economica',
        'artisticoculturais' :'artistico culturais',
        'conselhogeral' : 'conselho geral',
        'tecnicoadministrativos' : 'tecnico administrativos',
        'registradosacrescentando' : 'registrado sacrescentando',
        'ruralcontratadas' : 'rural contratadas',
        'estudantesconvenio' : 'estudantes convenio',
        'delegadode' : 'delegado de',
        'junhode' : 'junho de',
        'denovembro' : 'de novembro',
        'lavourapecuariafloresta' : 'lavoura pecuaria floresta',
        'autorizadaaltera' : 'autorizada altera',
        'grupodefesa' : 'grupo defesa',
        'contribuicoesprevidenciarias' : 'contribuicoes previdenciarias',
        'cpfgce' : 'cpf gce',
        'empregose' : 'empregos e',
        'dosanistiados' : 'dos anistiados',
        'antigomobilismo' : 'antigo mobilismo',
        'dogrupodirecao' : 'do grupo direcao',
        'milhoestrezentos' : 'milhoes trezentos',
        'deavaliacao' : 'de avaliacao',
        'salariominimo' : 'salario minimo',
        'imoveis' : 'imovel',
        'grupodirecao' : 'grupo direcao',
        'pispasep' : 'pis pasep',
        'rurais' : 'rural'
    }
    new_text = texto.split(" ")
    container =[]
    for word in new_text:
        if word in select_tokens.keys():
            container.append(select_tokens[word])
        else:
            container.append(word)
    return " ".join(container)

Função de pré-processamento

Por fim, vamos consolidar uma função que agrega as nossas etapas de pré-processamento.

def pre_processing_pipeline(texto:str):
    texto = " ".join([token for token in word_tokenize(removerAcentosECaracteresEspeciais(texto)) if not token.isdigit()])

    texto = " ".join([token for token in word_tokenize(texto) if not hasNumbers(token)])

    texto = " ".join([token for token in word_tokenize(texto) if len(token) > 2])
    texto = " ".join([split_selected_tokens(token) for token in word_tokenize(texto) if not token.isdigit()])
    return texto

Vamos retirar acentos e caracteres especiais do nosso conjunto de stopwords.

stopwords_2 = []
for word in stopwords: 
    stopwords_2.append(removerAcentosECaracteresEspeciais(word))

Feature Engineering

Uma vez determinado alguns passos adicionais de pré-processamento, podemos avaliar a construção de algumas novas features para serem incorporadas ao nosso modelo de dados. Inicialmente, vamos analisar como se distribuem o tamanho das ementas do nosso conjunto de treinamento.

text_size = pd.DataFrame(xTrain['description'].str.len()).rename(columns={'description' : 'size'}).reset_index()

O histograma informa que temos ementas com menos de 100 caracterters até algumas com mais de 500, podemos explorar essa diversidade de tamanhos como uma possível feature, por exemplo. Além disso, há outras características dos tokens que podem conferir informações que o modelo poderá usar para realizar a predição. Assim, iremos escrever algumas funções que irão nos auxiliar a consolidar novas features.

def add_new_text_features(dataframe: pd.DataFrame) -> pd.DataFrame:
    df = dataframe.copy()
    df['total_length'] = df['description'].apply(len)
    df['num_punctuation'] = df['description'].apply(lambda comment: sum(comment.count(w) for w in '.,;:'))
    df['num_symbols'] = df['description'].apply(lambda comment: sum(comment.count(w) for w in '*&$%'))
    df['num_words'] = df['description'].apply(lambda comment: len(comment.split()))
    df['num_unique_words'] = df['description'].apply(lambda comment: len(set(w for w in comment.split())))
    df['words_vs_unique'] = df['num_unique_words'] / df['num_words']
    return df

def add_datepart(dataframe, fldname, drop=True):
    """Função modificada da fast.ai"""
    df = dataframe.copy()
    fld = df[fldname]
    if not np.issubdtype(fld.dtype, np.datetime64):
        df[fldname] = fld = pd.to_datetime(fld, 
                                     infer_datetime_format=True)
    targ_pre = re.sub('[Dd]ate$', '', fldname)
    for n in ('Year', 'Month', 'Week', 'Day', 'Dayofweek', 
            'Dayofyear', 'Is_month_end', 'Is_month_start', 
            'Is_quarter_end', 'Is_quarter_start', 'Is_year_end', 
            'Is_year_start'):
        df[targ_pre+n] = getattr(fld.dt,n.lower())
    df[targ_pre+'Elapsed'] = fld.astype(np.int64) // 10**9
    if drop: df.drop(fldname, axis=1, inplace=True)
    for col in ['Is_month_end', 'Is_month_start', 'Is_quarter_end',
       'Is_quarter_start', 'Is_year_end',
       'Is_year_start']:
        df[targ_pre+col] = df[targ_pre+col].map({True: 1, False: 0})
    return df

def train_cats(dataframe):
    """
    *** função modificada da fast.ai ***
    Change any columns of strings in a panda's dataframe to a column of
    categorical values. This applies the changes inplace.
    Parameters:
    -----------
    df: A pandas dataframe. Any columns of strings will be changed to
        categorical values.
	"""
    df = dataframe.copy()
    for n,c in df.items():
        if is_string_dtype(c) and (n != 'description' and n != 'datePublished'): 
            df[n] = c.astype('category').cat.as_ordered()
    return df

def apply_cats(dataframe: pd.DataFrame, trn: pd.DataFrame):
    """
    *** função modificada da fast.ai ***
    Changes any columns of strings in df into categorical variables using trn as
    a template for the category codes.
    Parameters:
    -----------
    df: A pandas dataframe. Any columns of strings will be changed to
        categorical values. The category codes are determined by trn.
    trn: A pandas dataframe. When creating a category for df, it looks up the
        what the category's code were in trn and makes those the category codes for df.
    """
    df = dataframe.copy()
    for n,c in df.items():
        if (n in trn.columns) and (trn[n].dtype.name=='category'):
            df[n] = c.astype('category').cat.as_ordered()
            df[n].cat.set_categories(trn[n].cat.categories, ordered=True, inplace=True)
    return df

def numericalize(dataframe, col: pd.Series, name, max_n_cat=None):
    """
    *** função modificada da fast.ai ***
    Changes the column col from a categorical type to it's integer codes.
    Parameters:
    -----------
    df: A pandas dataframe. df[name] will be filled with the integer codes from
        col.
    col: The column you wish to change into the categories.
    name: The column name you wish to insert into df. This column will hold the
        integer codes.
    max_n_cat: If col has more categories than max_n_cat it will not change the
        it to its integer codes. If max_n_cat is None, then col will always be
        converted.
    """
    df = dataframe.copy()
    if not is_numeric_dtype(col) and ( max_n_cat is None or len(col.cat.categories)>max_n_cat):
        df[name] = pd.Categorical(col).codes+1
    return df

def pipeline_for_feature_eng(train_data: Tuple[pd.DataFrame, pd.DataFrame], mode: str, vect: TfidfVectorizer, multilabel: MultiLabelBinarizer, test_data: Tuple[pd.DataFrame, pd.DataFrame] = None):
    if mode == 'train':
        xTrain = train_data[0]
        yTrain = train_data[1]
        df_train = add_new_text_features(xTrain)
        df_train = train_cats(df_train)
        df_train = numericalize(df_train, df_train.legislationType, 'NUM_legislationType')
        df_train = add_datepart(df_train, 'datePublished')
        yTrain_transformed = multilabel.fit_transform(yTrain['keywords'])
        dtm_only_tokens = vect.fit_transform(df_train['description'])
        dtm_tokens_to_df = pd.DataFrame(dtm_only_tokens.toarray(), columns=vect.get_feature_names())
        dtm_tokens_to_df['legislationIdentifier'] = yTrain.index
        dtm_tokens_to_df.set_index('legislationIdentifier', inplace=True)
        xTrain_with_newft = df_train.merge(dtm_tokens_to_df, how='left', left_index=True, right_index=True).drop(['legislationType', 'description'], axis='columns')
        return xTrain_with_newft, yTrain_transformed, vect, multilabel
    if mode == 'test' or mode == 'validation':
        xTest = test_data[0]
        yTest = test_data[1]
        xTrain = train_data[0]
        df_test = add_new_text_features(xTest)
        df_test = apply_cats(df_test, xTrain)
        df_test = numericalize(df_test, df_test.legislationType, 'NUM_legislationType')
        df_test = add_datepart(df_test, 'datePublished')
        yTest_transformed = multilabel.fit_transform(yTest['keywords'])
        dtm_only_tokens = vect.fit_transform(df_test['description'])
        dtm_tokens_to_df = pd.DataFrame(dtm_only_tokens.toarray(), columns=vect.get_feature_names())
        dtm_tokens_to_df['legislationIdentifier'] = yTest.index
        dtm_tokens_to_df.set_index('legislationIdentifier', inplace=True)
        xTest_with_newft = df_test.merge(dtm_tokens_to_df, how='left', left_index=True, right_index=True).drop(['legislationType', 'description'], axis='columns')
        return xTest_with_newft, yTest_transformed

Assim, podemos conduzir as transformações necessárias no conjunto de treinamento e validação.

vect = TfidfVectorizer(
    strip_accents='unicode',
    analyzer='word',
    tokenizer=word_tokenize,
    max_features=5000,
    preprocessor=pre_processing_pipeline,
    stop_words=stopwords_2)
multilabel = MultiLabelBinarizer()
%%time
xTrain_with_newft, yTrain_transformed, vect, multilabel = pipeline_for_feature_eng((xTrain, yTrain), mode='train', vect=vect, multilabel=multilabel)
Wall time: 25 s
%%time
xValidation_with_newft, yValidation_transformed = pipeline_for_feature_eng((xTrain,), mode='test', vect=vect, multilabel=multilabel, test_data=(xValidation, yValidation))
Wall time: 10.8 s

Ao que parece temos um estratágia vencedora para melhorar a performance do nosso modelo.

Novamente, iremos instanciar um novo estimador e iniciar vamos avaliar se as novas etapas de pré-processamento e feature engineering beneficaram o modelo.

lr = LogisticRegression()
clf = OneVsRestClassifier(lr)
clf.fit(xTrain_with_newft, yTrain_transformed)
OneVsRestClassifier(estimator=LogisticRegression())
y_pred = clf.predict(xValidation_with_newft)

Ao que parece o feature engineering não surtiu muito efeito no Hamming loss. Mesmo assim devemos conferir o desempenho na métrica f1.

print_hamming_loss(yValidation_transformed, y_pred, clf)
Clf:  OneVsRestClassifier
Hamming loss: 0.10762477260143953

Olha o relatório! 😲😲😲😲 Com a inclusão das novas features, para regressão logística na realidade houve uma inclusão de ruído, do que propriarmente amplificação do sinal para que o modelo pudesse realizar a predição de forma adequada. Acho melhor seguirmos para avaliar um outro modelo ...

print(classification_report(yValidation_transformed, y_pred, target_names=mapping_tags, zero_division=0))
                                                           precision    recall  f1-score   support

                                     ACORDO INTERNACIONAL       0.00      0.00      0.00       505
                                                ALTERAÇÃO       0.00      0.00      0.00      1018
                                                   AMBITO       0.00      0.00      0.00       406
                                                APROVAÇÃO       0.00      0.00      0.00      1944
                                         AREA PRIORITARIA       0.00      0.00      0.00       783
                                                      ATO       0.00      0.00      0.00      1465
                                              AUTORIZAÇÃO       0.00      0.00      0.00       835
                                                   BRASIL       0.00      0.00      0.00       413
                                              COMPETENCIA       0.00      0.00      0.00       445
                                               COMPOSIÇÃO       0.00      0.00      0.00       310
                                                CONCESSÃO       0.00      0.00      0.00      1890
                                               CORRELAÇÃO       0.00      0.00      0.00       263
                                      CREDITO SUPLEMENTAR       0.00      0.00      0.00      1302
                                                  CRIAÇÃO       0.00      0.00      0.00       608
                                                CRITERIOS       0.00      0.00      0.00       390
                                               DECLARAÇÃO       0.00      0.00      0.00      1197
                                           DESAPROPRIAÇÃO       0.00      0.00      0.00      1023
                                               DESTINAÇÃO       0.00      0.00      0.00      1730
                                             DISPOSITIVOS       0.00      0.00      0.00       268
                                     DOTAÇÃO ORÇAMENTARIA       0.00      0.00      0.00      1153
                              EMPRESA DE TELECOMUNICAÇÕES       0.00      0.00      0.00      1749
                                ESTADO DE MINAS GERAIS MG       0.00      0.00      0.00       396
                                   ESTADO DE SÃO PAULO SP       0.00      0.00      0.00       494
                                      ESTADO DO PARANA PR       0.00      0.00      0.00       260
                           ESTADO DO RIO GRANDE DO SUL RS       0.00      0.00      0.00       223
                                                EXECUTIVO       0.00      0.00      0.00       258
                                                 EXECUÇÃO       0.00      0.00      0.00      1870
                                                  FIXAÇÃO       0.00      0.00      0.00       365
                                            FUNCIONAMENTO       0.00      0.00      0.00       305
                                                 HIPOTESE       0.00      0.00      0.00       228
                                             IMOVEL RURAL       0.00      0.00      0.00       793
INSTITUTO NACIONAL DE COLONIZAÇÃO E REFORMA AGRARIA INCRA       0.00      0.00      0.00       794
                                         INTERESSE SOCIAL       0.00      0.00      0.00       899
                                                MUNICIPIO       0.00      0.00      0.00      3258
                                                   NORMAS       0.00      0.00      0.00      1045
                                                 OBJETIVO       0.00      0.00      0.00       511
                           ORÇAMENTO DA SEGURIDADE SOCIAL       0.00      0.00      0.00       323
                                         ORÇAMENTO FISCAL       0.00      0.00      0.00       651
                                         PAIS ESTRANGEIRO       0.00      0.00      0.00       430
                                             RADIODIFUSÃO       0.00      0.00      0.00      1749
                                          REFORMA AGRARIA       0.00      0.00      0.00       793
                                                  REFORÇO       0.00      0.00      0.00      1124
                                                RENOVAÇÃO       0.00      0.00      0.00       631
                                                  SERVIÇO       0.00      0.00      0.00      1832
                                                    TEXTO       0.00      0.00      0.00       348
                                            UNIÃO FEDERAL       0.00      0.00      0.00      1224
                                        UTILIDADE PUBLICA       0.00      0.00      0.00       320

                                                micro avg       0.00      0.00      0.00     40821
                                                macro avg       0.00      0.00      0.00     40821
                                             weighted avg       0.00      0.00      0.00     40821
                                              samples avg       0.00      0.00      0.00     40821

Random Forest

Vamos partir para avaliar nossos dados com 🌳RandomForest🎲 que é considerado um ensemble de Decision Trees🌳.

clf = RandomForestClassifier(
    n_estimators=10, 
    min_samples_leaf= 1,
    max_features=0.5,
    class_weight=None,
    bootstrap=True,
    oob_score=True
)
clf.fit(xTrain_with_newft, yTrain_transformed)
D:\opensource\gov-data-product\.venv\lib\site-packages\sklearn\ensemble\_forest.py:540: UserWarning: Some inputs do not have OOB scores. This probably means too few trees were used to compute any reliable oob estimates.
  warn("Some inputs do not have OOB scores. "
D:\opensource\gov-data-product\.venv\lib\site-packages\sklearn\ensemble\_forest.py:545: RuntimeWarning: invalid value encountered in true_divide
  predictions[k].sum(axis=1)[:, np.newaxis])
RandomForestClassifier(max_features=0.5, n_estimators=10, oob_score=True)

Inspecionando o oob score - que é o erro médio calculado para cada predição a partir das árvores que não fazem parte da respectiva amostragem boostrap, para maiores detalhes ler a documentação aqui - temos um sinal favorável de uma performance melhor do que tivemos até o momento.

clf.oob_score_
0.9833852508934451

Todavia, como recebemos um warning na hora do fit do modelo, informando que é possível que não tenhamos uma estimativa confiável do oob score, devido a um número pequeno de arvóres (n=10) para realizar o cálculo, então é melhor verificarmos outras métricas. Vamos avaliar o modelo com os nosso cojunto de validação.

y_pred = clf.predict(xValidation_with_newft)
print_hamming_loss(yValidation_transformed, y_pred, clf)
Clf:  RandomForestClassifier
Hamming loss: 0.10424740963378945

Tivemos uma leve melhora do hamming loss, todavia, o nosso f1-micro marcou apenas míseros 0.13 😥. Precisamos continuar trabalhando na melhoria do nosso modelo 👷.

print(classification_report(yValidation_transformed, y_pred, target_names=mapping_tags, zero_division=0))
                                                           precision    recall  f1-score   support

                                     ACORDO INTERNACIONAL       0.67      0.00      0.01       505
                                                ALTERAÇÃO       0.07      0.00      0.00      1018
                                                   AMBITO       0.07      0.00      0.01       406
                                                APROVAÇÃO       1.00      0.00      0.00      1944
                                         AREA PRIORITARIA       0.00      0.00      0.00       783
                                                      ATO       0.00      0.00      0.00      1465
                                              AUTORIZAÇÃO       0.08      0.00      0.01       835
                                                   BRASIL       0.00      0.00      0.00       413
                                              COMPETENCIA       0.00      0.00      0.00       445
                                               COMPOSIÇÃO       0.00      0.00      0.00       310
                                                CONCESSÃO       0.44      0.00      0.01      1890
                                               CORRELAÇÃO       0.20      0.01      0.01       263
                                      CREDITO SUPLEMENTAR       1.00      0.00      0.01      1302
                                                  CRIAÇÃO       0.00      0.00      0.00       608
                                                CRITERIOS       0.11      0.02      0.04       390
                                               DECLARAÇÃO       0.00      0.00      0.00      1197
                                           DESAPROPRIAÇÃO       0.00      0.00      0.00      1023
                                               DESTINAÇÃO       0.98      0.27      0.42      1730
                                             DISPOSITIVOS       0.00      0.00      0.00       268
                                     DOTAÇÃO ORÇAMENTARIA       0.98      0.81      0.89      1153
                              EMPRESA DE TELECOMUNICAÇÕES       0.00      0.00      0.00      1749
                                ESTADO DE MINAS GERAIS MG       0.00      0.00      0.00       396
                                   ESTADO DE SÃO PAULO SP       0.00      0.00      0.00       494
                                      ESTADO DO PARANA PR       0.00      0.00      0.00       260
                           ESTADO DO RIO GRANDE DO SUL RS       0.00      0.00      0.00       223
                                                EXECUTIVO       0.00      0.00      0.00       258
                                                 EXECUÇÃO       0.33      0.00      0.00      1870
                                                  FIXAÇÃO       0.25      0.01      0.02       365
                                            FUNCIONAMENTO       0.00      0.00      0.00       305
                                                 HIPOTESE       0.00      0.00      0.00       228
                                             IMOVEL RURAL       0.00      0.00      0.00       793
INSTITUTO NACIONAL DE COLONIZAÇÃO E REFORMA AGRARIA INCRA       0.00      0.00      0.00       794
                                         INTERESSE SOCIAL       0.00      0.00      0.00       899
                                                MUNICIPIO       0.45      0.00      0.01      3258
                                                   NORMAS       0.19      0.28      0.22      1045
                                                 OBJETIVO       0.00      0.00      0.00       511
                           ORÇAMENTO DA SEGURIDADE SOCIAL       0.00      0.00      0.00       323
                                         ORÇAMENTO FISCAL       0.00      0.00      0.00       651
                                         PAIS ESTRANGEIRO       0.00      0.00      0.00       430
                                             RADIODIFUSÃO       0.00      0.00      0.00      1749
                                          REFORMA AGRARIA       0.00      0.00      0.00       793
                                                  REFORÇO       0.99      0.83      0.90      1124
                                                RENOVAÇÃO       0.00      0.00      0.00       631
                                                  SERVIÇO       0.00      0.00      0.00      1832
                                                    TEXTO       0.00      0.00      0.00       348
                                            UNIÃO FEDERAL       0.94      0.15      0.26      1224
                                        UTILIDADE PUBLICA       0.00      0.00      0.00       320

                                                micro avg       0.64      0.07      0.13     40821
                                                macro avg       0.19      0.05      0.06     40821
                                             weighted avg       0.30      0.07      0.08     40821
                                              samples avg       0.21      0.10      0.13     40821

Feature Importance

Vamos dar continuidade a nossa saga de desenvolver um produto data-driven orientados por dados governamentais, a partir da análise das feature importance do nosso modelo.

def rf_feat_importance(m, df):
    """*** função da fastai"""
    return pd.DataFrame({'feature':df.columns, 'imp':m.feature_importances_}
                       ).sort_values('imp', ascending=False)
features_analise = pd.DataFrame(xTrain_with_newft)
feature_importances = rf_feat_importance(clf, features_analise)

Pela análise do gráfico abaixo, a boa notícia é que algumas das features criadas, como datePublishedElapsed e datePublishedYear parecem estar ajudando o modelo no trabalho de predição.

A próxima experimentação é para validar a seguinte premissa: É possível que temos features demais no nosso modelo? Assim, iremos manter as features criadas e iremos realizar um corte nos tokens que possuem uma feature importance menor que 0.001.

num_features = {'total_length', 'num_punctuation', 'num_symbols', 'num_words',
       'num_unique_words', 'words_vs_unique', 'NUM_legislationType',
       'datePublishedYear', 'datePublishedMonth', 'datePublishedWeek',
       'datePublishedDay', 'datePublishedDayofweek', 'datePublishedDayofyear',
       'datePublishedIs_month_end', 'datePublishedIs_month_start',
       'datePublishedIs_quarter_end', 'datePublishedIs_quarter_start',
       'datePublishedIs_year_end', 'datePublishedIs_year_start',
       'datePublishedElapsed'}
new_stop_words = list(set(feature_importances[feature_importances['imp'] <= 0.001]['feature'].unique())-num_features)
stopwords_3 = stopwords_2.copy()
stopwords_3.extend(new_stop_words)

Dessa forma, iremos instanciar novamente o TfidfVectorizer limitando o processo de vetorização a 1000 features e passando uma lista de stopwords que inclua aqueles tokens que tiverem feature importance menor que 0.001.

vect = TfidfVectorizer(
    strip_accents='unicode',
    analyzer='word',
    tokenizer=word_tokenize,
    max_features=1000,
    preprocessor=pre_processing_pipeline,
    stop_words=stopwords_3)
multilabel = MultiLabelBinarizer()
xTrain_with_newft, yTrain_transformed, vect, multilabel = pipeline_for_feature_eng((xTrain, yTrain), mode='train', vect=vect, multilabel=multilabel)
xValidation_with_newft, yValidation_transformed = pipeline_for_feature_eng((xTrain,), mode='test', vect=vect, multilabel=multilabel, test_data=(xValidation, yValidation))

Procedemos instanciando um novo RandomForestClassifier só que dessa vez aumentamos o número de estimadores para 60. Em seguida, procedemos com a avaliação dos efeitos das nossas modificações.

clf = RandomForestClassifier(
    n_estimators=60, 
    min_samples_leaf= 1,
    max_features=0.5,
    class_weight=None,
    bootstrap=True
)
clf.fit(xTrain_with_newft, yTrain_transformed)
y_pred = clf.predict(xValidation_with_newft)
RandomForestClassifier(max_features=0.5, n_estimators=60)
print_hamming_loss(yValidation_transformed, y_pred, clf)
Clf:  RandomForestClassifier
Hamming loss: 0.10842890664135622
print(classification_report(yValidation_transformed, y_pred, target_names=mapping_tags, zero_division=0))
                                                           precision    recall  f1-score   support

                                     ACORDO INTERNACIONAL       0.00      0.00      0.00       505
                                                ALTERAÇÃO       0.33      0.01      0.01      1018
                                                   AMBITO       0.13      0.00      0.01       406
                                                APROVAÇÃO       0.50      0.00      0.00      1944
                                         AREA PRIORITARIA       0.00      0.00      0.00       783
                                                      ATO       0.00      0.00      0.00      1465
                                              AUTORIZAÇÃO       0.17      0.03      0.05       835
                                                   BRASIL       0.00      0.00      0.00       413
                                              COMPETENCIA       0.00      0.00      0.00       445
                                               COMPOSIÇÃO       0.00      0.00      0.00       310
                                                CONCESSÃO       0.50      0.00      0.00      1890
                                               CORRELAÇÃO       0.25      0.00      0.01       263
                                      CREDITO SUPLEMENTAR       0.00      0.00      0.00      1302
                                                  CRIAÇÃO       0.12      0.00      0.00       608
                                                CRITERIOS       0.23      0.05      0.08       390
                                               DECLARAÇÃO       0.50      0.00      0.00      1197
                                           DESAPROPRIAÇÃO       0.00      0.00      0.00      1023
                                               DESTINAÇÃO       0.00      0.00      0.00      1730
                                             DISPOSITIVOS       0.50      0.00      0.01       268
                                     DOTAÇÃO ORÇAMENTARIA       0.00      0.00      0.00      1153
                              EMPRESA DE TELECOMUNICAÇÕES       1.00      0.00      0.00      1749
                                ESTADO DE MINAS GERAIS MG       0.00      0.00      0.00       396
                                   ESTADO DE SÃO PAULO SP       0.00      0.00      0.00       494
                                      ESTADO DO PARANA PR       0.00      0.00      0.00       260
                           ESTADO DO RIO GRANDE DO SUL RS       0.00      0.00      0.00       223
                                                EXECUTIVO       0.00      0.00      0.00       258
                                                 EXECUÇÃO       0.86      0.00      0.01      1870
                                                  FIXAÇÃO       0.00      0.00      0.00       365
                                            FUNCIONAMENTO       0.00      0.00      0.00       305
                                                 HIPOTESE       0.50      0.01      0.02       228
                                             IMOVEL RURAL       0.00      0.00      0.00       793
INSTITUTO NACIONAL DE COLONIZAÇÃO E REFORMA AGRARIA INCRA       0.00      0.00      0.00       794
                                         INTERESSE SOCIAL       0.00      0.00      0.00       899
                                                MUNICIPIO       0.79      0.00      0.01      3258
                                                   NORMAS       0.36      0.16      0.22      1045
                                                 OBJETIVO       0.00      0.00      0.00       511
                           ORÇAMENTO DA SEGURIDADE SOCIAL       0.00      0.00      0.00       323
                                         ORÇAMENTO FISCAL       0.00      0.00      0.00       651
                                         PAIS ESTRANGEIRO       0.00      0.00      0.00       430
                                             RADIODIFUSÃO       1.00      0.00      0.00      1749
                                          REFORMA AGRARIA       0.00      0.00      0.00       793
                                                  REFORÇO       0.00      0.00      0.00      1124
                                                RENOVAÇÃO       0.00      0.00      0.00       631
                                                  SERVIÇO       1.00      0.00      0.00      1832
                                                    TEXTO       0.00      0.00      0.00       348
                                            UNIÃO FEDERAL       0.00      0.00      0.00      1224
                                        UTILIDADE PUBLICA       0.00      0.00      0.00       320

                                                micro avg       0.31      0.01      0.01     40821
                                                macro avg       0.19      0.01      0.01     40821
                                             weighted avg       0.33      0.01      0.01     40821
                                              samples avg       0.03      0.01      0.02     40821

É melhor seguir em frente e não pensar muito nessa tragédia ... 😕

ExtraTreesClassifier 🌳🔀

Uma das possíveis causas do nosso desempenho ruim, pode ser devido a um overfitting. Assim, iremos recorrer a uma variante da RandomForest implementada pelo sklearn chamado ExtraTreesClassifier que diminui possíveis efeitos de overfitting.

clf = ExtraTreesClassifier(
    min_samples_leaf= 3,
    max_features=0.5,
    n_jobs=-1
)

vect = TfidfVectorizer(
    strip_accents='unicode',
    analyzer='word',
    tokenizer=word_tokenize,
    max_features=5000,
    preprocessor=pre_processing_pipeline,
    stop_words=stopwords_2)
multilabel = MultiLabelBinarizer()

xTrain_with_newft, yTrain_transformed, vect, multilabel = pipeline_for_feature_eng((xTrain, yTrain), mode='train', vect=vect, multilabel=multilabel)
xValidation_with_newft, yValidation_transformed = pipeline_for_feature_eng((xTrain,), mode='test', vect=vect, multilabel=multilabel, test_data=(xValidation, yValidation))
clf.fit(xTrain_with_newft, yTrain_transformed)
y_pred = clf.predict(xValidation_with_newft)

Infelizmente, nosso modelo cotinua com um um desempenho pífio. Em 85% das predições são nulas. Além disso, registramos um f1-micro de 0.08.

len([data for data in y_pred if ~data.any()])/y_pred.shape[0]
0.8542750929368029
print(classification_report(yValidation_transformed, y_pred, target_names=mapping_tags, zero_division=0))
                                                           precision    recall  f1-score   support

                                     ACORDO INTERNACIONAL       0.29      0.00      0.01       505
                                                ALTERAÇÃO       0.23      0.01      0.01      1018
                                                   AMBITO       0.00      0.00      0.00       406
                                                APROVAÇÃO       1.00      0.00      0.00      1944
                                         AREA PRIORITARIA       0.00      0.00      0.00       783
                                                      ATO       0.00      0.00      0.00      1465
                                              AUTORIZAÇÃO       0.07      0.00      0.01       835
                                                   BRASIL       0.00      0.00      0.00       413
                                              COMPETENCIA       0.00      0.00      0.00       445
                                               COMPOSIÇÃO       0.00      0.00      0.00       310
                                                CONCESSÃO       0.00      0.00      0.00      1890
                                               CORRELAÇÃO       0.00      0.00      0.00       263
                                      CREDITO SUPLEMENTAR       0.00      0.00      0.00      1302
                                                  CRIAÇÃO       0.00      0.00      0.00       608
                                                CRITERIOS       0.00      0.00      0.00       390
                                               DECLARAÇÃO       0.00      0.00      0.00      1197
                                           DESAPROPRIAÇÃO       0.00      0.00      0.00      1023
                                               DESTINAÇÃO       0.00      0.00      0.00      1730
                                             DISPOSITIVOS       0.00      0.00      0.00       268
                                     DOTAÇÃO ORÇAMENTARIA       0.98      0.76      0.85      1153
                              EMPRESA DE TELECOMUNICAÇÕES       0.00      0.00      0.00      1749
                                ESTADO DE MINAS GERAIS MG       0.00      0.00      0.00       396
                                   ESTADO DE SÃO PAULO SP       0.00      0.00      0.00       494
                                      ESTADO DO PARANA PR       0.00      0.00      0.00       260
                           ESTADO DO RIO GRANDE DO SUL RS       0.00      0.00      0.00       223
                                                EXECUTIVO       1.00      0.02      0.03       258
                                                 EXECUÇÃO       0.00      0.00      0.00      1870
                                                  FIXAÇÃO       0.00      0.00      0.00       365
                                            FUNCIONAMENTO       0.00      0.00      0.00       305
                                                 HIPOTESE       0.00      0.00      0.00       228
                                             IMOVEL RURAL       0.00      0.00      0.00       793
INSTITUTO NACIONAL DE COLONIZAÇÃO E REFORMA AGRARIA INCRA       0.00      0.00      0.00       794
                                         INTERESSE SOCIAL       0.00      0.00      0.00       899
                                                MUNICIPIO       0.00      0.00      0.00      3258
                                                   NORMAS       0.45      0.07      0.12      1045
                                                 OBJETIVO       0.00      0.00      0.00       511
                           ORÇAMENTO DA SEGURIDADE SOCIAL       0.00      0.00      0.00       323
                                         ORÇAMENTO FISCAL       0.00      0.00      0.00       651
                                         PAIS ESTRANGEIRO       0.00      0.00      0.00       430
                                             RADIODIFUSÃO       0.00      0.00      0.00      1749
                                          REFORMA AGRARIA       0.00      0.00      0.00       793
                                                  REFORÇO       0.99      0.73      0.84      1124
                                                RENOVAÇÃO       0.00      0.00      0.00       631
                                                  SERVIÇO       0.00      0.00      0.00      1832
                                                    TEXTO       0.00      0.00      0.00       348
                                            UNIÃO FEDERAL       0.00      0.00      0.00      1224
                                        UTILIDADE PUBLICA       0.00      0.00      0.00       320

                                                micro avg       0.89      0.04      0.08     40821
                                                macro avg       0.11      0.03      0.04     40821
                                             weighted avg       0.13      0.04      0.05     40821
                                              samples avg       0.12      0.06      0.08     40821

Chengando a um nível de desespero sem precedentes no desenvolvimento do nosso projeto, iremos recorrer a uma quasi força bruta (RandomizedSearchCV) para tentarmos otimizar os hiperparâmetros da nossa ExtraTreesClassifier.

Definimos o espaço amostral de possíveis valores para cada um dos hiperparâmetros que buscamos otimizar.

rf_p_dist = {
    'min_samples_leaf' : [1, 3, 5, 10, 25],
    'max_features' : [0.5, 1, 'log2', "sqrt"],
    'max_depth' : [None, 3, 5, 10],
    'n_estimators' : [100, 200, 300, 400, 500],
    'criterion' : ['gini', 'entropy'],
    'bootstrap' : [True, False]
}

Em seguida, definimos uma função que resultará nos melhores parâmetros, bem como o score obtido.

def hypertuning_rscv(est, p_dist, nbr_iter, X, Y):
    rdmsearch = RandomizedSearchCV(est, param_distributions=p_dist, n_jobs=-1, n_iter=nbr_iter, cv=9, scoring='f1_micro')
    rdmsearch.fit(X, Y)
    ht_params = rdmsearch.best_params_
    ht_score = rdmsearch.best_score_
    return ht_params, ht_score
%%time
clf = ExtraTreesClassifier(n_jobs=-1)
rf_parameters, rf_ht_score = hypertuning_rscv(clf, rf_p_dist, 40, xTrain_with_newft, yTrain_transformed)
Wall time: 9h 27min 40s

Ao fim de 9 fucking hours 😫 chegamos aos parâmetros de ouro 🥇!!

rf_parameters
{'n_estimators': 500,
 'min_samples_leaf': 1,
 'max_features': 0.5,
 'max_depth': None,
 'criterion': 'gini',
 'bootstrap': False}

Um f1-micro de 0.93 obtido com cross-validation parece promissor 🤩.

rf_ht_score
0.9364780159858336

Finalmente, vamos instanciar um novo estimador com os parâmetos obtidos na etapa anterior.

clf = ExtraTreesClassifier(
    n_estimators=rf_parameters.get('n_estimators'),
    min_samples_leaf=rf_parameters.get('min_samples_leaf'),
    max_features=rf_parameters.get('max_features'),
    max_depth=rf_parameters.get('max_depth'),
    criterion=rf_parameters.get('criterion'),
    bootstrap=rf_parameters.get('bootstrap'),
    n_jobs=-1
)
%%time
clf.fit(xTrain_with_newft, yTrain_transformed)
Wall time: 12min 47s
ExtraTreesClassifier(max_features=0.5, n_estimators=500, n_jobs=-1)

Finalmente, faremos uma avaliação do modelo com os dados de validação.

y_pred = clf.predict(xValidation_with_newft)

E mais uma vez, o nosso modelo está péssimo 🤬. Temos 67% de predições nulas, além de um f1-micro de 0.10.

len([data for data in y_pred if ~data.any()])/y_pred.shape[0]
0.6711276332094176
print(classification_report(yValidation_transformed, y_pred, target_names=mapping_tags, zero_division=0))
                                                           precision    recall  f1-score   support

                                     ACORDO INTERNACIONAL       0.29      0.00      0.01       505
                                                ALTERAÇÃO       0.17      0.00      0.01      1018
                                                   AMBITO       0.00      0.00      0.00       406
                                                APROVAÇÃO       1.00      0.00      0.00      1944
                                         AREA PRIORITARIA       0.00      0.00      0.00       783
                                                      ATO       0.00      0.00      0.00      1465
                                              AUTORIZAÇÃO       0.07      0.00      0.01       835
                                                   BRASIL       0.00      0.00      0.00       413
                                              COMPETENCIA       0.00      0.00      0.00       445
                                               COMPOSIÇÃO       0.00      0.00      0.00       310
                                                CONCESSÃO       0.00      0.00      0.00      1890
                                               CORRELAÇÃO       0.00      0.00      0.00       263
                                      CREDITO SUPLEMENTAR       1.00      0.00      0.00      1302
                                                  CRIAÇÃO       0.18      0.00      0.01       608
                                                CRITERIOS       0.07      0.00      0.00       390
                                               DECLARAÇÃO       0.00      0.00      0.00      1197
                                           DESAPROPRIAÇÃO       0.00      0.00      0.00      1023
                                               DESTINAÇÃO       0.95      0.03      0.07      1730
                                             DISPOSITIVOS       0.00      0.00      0.00       268
                                     DOTAÇÃO ORÇAMENTARIA       0.98      0.85      0.91      1153
                              EMPRESA DE TELECOMUNICAÇÕES       0.00      0.00      0.00      1749
                                ESTADO DE MINAS GERAIS MG       0.00      0.00      0.00       396
                                   ESTADO DE SÃO PAULO SP       0.00      0.00      0.00       494
                                      ESTADO DO PARANA PR       0.00      0.00      0.00       260
                           ESTADO DO RIO GRANDE DO SUL RS       0.00      0.00      0.00       223
                                                EXECUTIVO       1.00      0.03      0.06       258
                                                 EXECUÇÃO       0.00      0.00      0.00      1870
                                                  FIXAÇÃO       0.11      0.00      0.01       365
                                            FUNCIONAMENTO       0.00      0.00      0.00       305
                                                 HIPOTESE       0.17      0.00      0.01       228
                                             IMOVEL RURAL       0.00      0.00      0.00       793
INSTITUTO NACIONAL DE COLONIZAÇÃO E REFORMA AGRARIA INCRA       0.00      0.00      0.00       794
                                         INTERESSE SOCIAL       0.00      0.00      0.00       899
                                                MUNICIPIO       0.00      0.00      0.00      3258
                                                   NORMAS       0.17      0.23      0.19      1045
                                                 OBJETIVO       0.00      0.00      0.00       511
                           ORÇAMENTO DA SEGURIDADE SOCIAL       0.00      0.00      0.00       323
                                         ORÇAMENTO FISCAL       0.00      0.00      0.00       651
                                         PAIS ESTRANGEIRO       0.00      0.00      0.00       430
                                             RADIODIFUSÃO       0.00      0.00      0.00      1749
                                          REFORMA AGRARIA       0.00      0.00      0.00       793
                                                  REFORÇO       0.99      0.85      0.91      1124
                                                RENOVAÇÃO       0.00      0.00      0.00       631
                                                  SERVIÇO       0.00      0.00      0.00      1832
                                                    TEXTO       0.00      0.00      0.00       348
                                            UNIÃO FEDERAL       0.87      0.01      0.02      1224
                                        UTILIDADE PUBLICA       0.00      0.00      0.00       320

                                                micro avg       0.62      0.06      0.10     40821
                                                macro avg       0.17      0.04      0.05     40821
                                             weighted avg       0.23      0.06      0.06     40821
                                              samples avg       0.16      0.08      0.10     40821

Há um forte indício de overfitting já que não conseguimos generalizar o suficiente o nosso modelo, evidenciada pela performance de predições no conjunto de validação. O presente projeto nasceu devido a minha participação em um curso de Machine Learning da ENAP e como tenho um prazo para entrega (que já está se esgostando) do produto final, preciso otimizar meu tempo. Portanto, precisamos de um "tiro certeiro" para o sucesso desse produto. Esse post foi um registro do que não deu certo na construção de nosso produto, registros de falhas e percalços não é comum em nossa cultura. Todavia, acredito que isso é valioso, além de ser real. Nem todo projeto é uma linha reta de apenas sucessos, a grande maioria das execuções possuem mais falhas que sucessos. Espero que vocês tenham gostado de conhecer os detalhes nada glamurosos da nossa caminhada. Assim, no próximo post, iremos treinar uma rede neural para classificar os normativos jurídicos como pretendido! Até a próxima 👨!!