UNIVERSIDADE FEDERAL DE GOIÁS ESCOLA DE ENGENHARIA ELÉTRICA, MECÂNICA E DE COMPUTAÇÃO CURSO DE GRADUAÇÃO EM ENGENHARIA DE COMPUTAÇÃO Datascience para o Mercado Financeiro Modelos de Machine Learning Lara Portilho Marques Fernanda de Castro Fernandes Orientador: Thyago Carvalho Marques Goiânia Novembro/2022 Índice ● ● ● Big Data Analytics Modelos Supervisionados ○ KNN ○ SVM ○ Naive Bayes ○ Random Forest ○ Redes Neurais Artificiais Modelos Não supervisionado ○ Processamento de linguagem natural ○ Redes Neurais Profundas ○ K-Means Clustering ○ Agglomerative Clustering ○ DBSCAN Big Data Analytics Pode ser definido como os datasets cujo tamanho ou tipo estão além da habilidade de um banco de dados relacional tradicional de captura, gerenciamento e processamento de dados com baixa latência. Esses datasets incluem dados estruturados, semi-estruturados e não estruturados, de diferentes fontes e tamanhos.Tais dados vêm de diferentes fontes, como dispositivos móveis, redes sociais, IoT, gerando um altíssimo volume de informações. Há várias utilizações do Big Data Analytics, como fonte de tomadas de decisão, modelagem e predição de possíveis consequências futuras, fortalecimento da inteligência de mercado (B.I). Outro benefício é a redução de custo e aumento da eficiência operacional, tendo em vista que um processamento flexível de dados pode ser mais econômico ao armazenar e analisar grandes volumes de dados. Aprendizado Supervisionado x Aprendizado Não Supervisionado Antes de falar sobre os tipos de aprendizado de máquina, é preciso definir o que é o Machine Learning (aprendizado de máquina ou ML). É definido por um sistema computacional que deve realizar uma tarefa, aprendendo a partir de uma experiência, enquanto procura melhorar sua performance. Em outras palavras, um algoritmo pode aprender a realizar algo partindo de um grande volume de inputs (dados), que representam as experiências da máquina. O aprendizado visa um generalismo que se encaixe na maioria dos casos, obtendo uma boa performance, e para isso, quanto mais dados melhor. Sabendo disso, pode-se passar para os tipos de Machine Learning. O primeiro é o aprendizado supervisionado: tenta-se prever uma variável dependente a partir de outras variáveis independentes. No treinamento de modelos supervisionados, o dataset utilizado contém a resposta desejada, seja a classificação a ser prevista ou o dado faltante buscado. O outro tipo abordado neste documento é o aprendizado não supervisionado. Este tipo trabalha com problemas em que a posse de dados anotados (com resposta) são inviáveis, seja pelo custo ou pela mera impossibilidade de obtenção. Algumas soluções para tais problemas são a observação em busca de padrões de repetição, de associação entre ações, em suma, a observação para captar alguma regra associativa entre os dados presentes. Modelos Supervisionados KNN (K-Nearest Neighbors) KNN é um tipo de algoritmo de aprendizagem supervisionada usado no campo de data mining e machine learning. Ele funciona como um classificador no qual o aprendizado é baseado em quão similar um dado ou vetor é de outro. É usado principalmente em problemas de classificação e de regressão. Sua modelagem matemática é feita pelo cálculo da distância entre os grupos já conhecidos e o novo objeto que se deseja inserir no dataset. Com isso, pode-se prever o comportamento de tal objeto, tendo como base a análise de um número pré determinado de amostras com menor distância do novo ponto. Esse número K de vizinhos é definido no treinamento do modelo e dá nome ao algoritmo. Algumas das aplicações de KNN no mundo real são sistemas de recomendação, como o utilizado pelo YouTube, Netflix e outros serviços de streaming. Também como ferramenta de busca por documentos semanticamente semelhantes, sendo eficaz em busca de plágio, por exemplo. Detecção de fraudes de cartão de crédito por reconhecimento de padrões de comportamento, análise de escore de crédito para empréstimos, predição de preços de ações, são alguns dos múltiplos usos cotidianos de KNN. São vantagens desse modelo a sua simplicidade na implementação e teoria, trazendo bons resultados em várias aplicações. Enquanto isso, suas desvantagens são a sensibilidade a ruídos nos dados e o custo computacional (tanto em complexidade de tempo quanto de espaço), dado que a cada adição, deve-se calcular a distância entre todos os outros exemplos (o algoritmo memoriza o treinamento, toda nova predição usa toda a base para realizar as comparações esperadas). Entretanto, isso pode ser minimizado pela criação de um centróide para cada grupo catalogado. Além disso, o algoritmo não é sensível aos outliers, pontos de uma classe que são mais próximos de outra classe do que com a original, parecendo ser da outra. O cálculo da distância é simples, podendo ser a clássica distância euclidiana ou outras, como a Manhattan, Hamming, Minkowski, Chebyshev, dentre outras. Algumas das fórmulas de distância bastante utilizadas: Distância Euclidiana: Distância Manhattan: Sendo ‘a’ e ‘b’ vetores de N dimensões, a distância manhattan é calculada pela soma dos módulos das diferenças entre cada posição dos vetores. Distância Minkowski: É uma distância pensada para espaços vetoriais de valores reais. É entendida como uma generalização das duas distâncias anteriormente comentadas, pois quando p=1, obtemos o resultado da Manhattan, e quando p=2, obtemos a distância euclidiana. Passos para implementação: 1. Ler o dataset que será usado, ver a quantidade de pontos existentes e quantas características são relevantes para o modelo. 2. Separar o dataset em inputs e targets, sendo o target a coluna que se deseja prever, enquanto o input serão todas as outras colunas. 3. Dividir os dados presentes no dataset para o treinamento e testagem. Com isso, pode-se ter noção de como o modelo performa com dados não vistos. 4. Construção e treinamento do modelo, definindo o K para maior acurácia. Sugestão: escolher K ímpar ou primo a quantidade de grupos conhecidos. 5. Testagem e checagem da acurácia. Observação: existe um método de validação do modelo que se chama Cross-Validation, que é a divisão do dataset em P partes. Uma parte será usada para treinamento e as outras serão os grupos de testagem, resultando numa métrica de acurácia. Então o processo é repetido até que todas as partes tenham sido usadas no treinamento do modelo. Com esse método, temos mais conhecimento de como o modelo se porta com novos dados, visto que o processo de divisões no dataset tem influência na performance do modelo. KNN para classificação: # KNN library(caret) library(pROC) library(mlbench) # Example-1 Classification data <read.csv('https://raw.githubusercontent.com/bkrai/Statistical-Modeling-a nd-Graphs-with-R/main/binary.csv') str(data) data$admit[data$admit == 0] <- 'no' data$admit[data$admit == 1] <- 'yes' data$admit <- factor(data$admit) data$rank <- factor(data$rank) # Data partition set.seed(1234) ind <- sample(2, nrow(data), replace = T, prob=c(.7, .3)) training <- data[ind==1, ] test <- data[ind==2, ] # K-NN trControl <- trainControl(method = "repeatedcv", #repeated cross-validation number = 10, # number of resampling iterations repeats = 3, # sets of folds to for repeated cross-validation classProbs = TRUE, summaryFunction = twoClassSummary) # classProbs needed for ROC set.seed(1234) fit <- train(admit ~ ., data = training, tuneGrid = expand.grid(k = 1:50), method = "knn", tuneLength = 20, metric = "ROC", trControl = trControl, preProc = c("center", "scale")) # necessary task # Model performance fit plot(fit) varImp(fit) pred <- predict(fit, newdata = test ) confusionMatrix(pred, test$admit, positive = 'yes' ) SVM - Support Vector Machine É um algoritmo de aprendizado de máquina supervisionado que pode ser usado para desafios de classificação ou regressão, com maior foco no treinamento e classificação de um dataset. Uma SVM trabalha com a construção de hiperplanos em um espaço n-dimensional para classificar ou regredir dados. Aqui, cada item de dados é plotado como um ponto no espaço n-dimensional (n é o número de recursos que você tem) com o valor de cada recurso ocupando a posição de uma coordenada. Então, buscamos o hiperplano que melhor diferencia as duas classes. Os vetores de suportes que dão nome ao algoritmo são as coordenadas de observação individual, com a SVM sendo, de certa forma, uma fronteira que separa as classes. Para definir o melhor hiperplano, procuramos o que tem a melhor margem de distância entre a fronteira e os grupos. Entretanto, resta saber como lidar com os outliers, os pontos de uma classe que se encontram no território de outra. A SVM tem um recurso de ignorar pontos ou valores muito discrepantes e ainda encontrar o hiperplano que tem margem máxima, portanto sendo robusto a outliers. Há também casos em que o plano linear a qual estamos acostumados a pensar não aceita um vetor de suporte que realmente classifique o dataset. Para isso, há o truque de kernel, uma ferramenta advinda da álgebra linear para a transformação do espaço vetorial em outra base a fim de que consigamos segregar corretamente os dados no espaço. Essa conversão entre os espaços de entrada dimensional baixa para um outro de nível superior serve para a conversão de problemas não separáveis para separáveis. A ferramenta usada são funções chamadas de núcleo. Por fim, as estimativas de probabilidade não são oferecidas diretamente, sendo necessário calcular por validações cruzadas. Vantagens: ● Boa funcionalidade com margens de separação claras entre os grupos; ● ● Eficaz quando o número de dimensões é maior que o número de amostras; Uso de subconjuntos de pontos de treinamento na função de decisão(vetores de suporte), tendo uma certa eficiência em termos de memória; Desvantagens: ● Desempenho aquém quando se há um grande conjunto de dados já que será necessário um grande tempo para treinamento do modelo; ● Não funciona muito bem com conjuntos de dados mais sujos, com maior ruído, onde as classes estão mais sobrepostas; Modelagem matemática de SVM Um hiperplano é definido pela equação w.x + b = 0, sendo derivada de vetores bidimensionais x = (x1,x2) e w = (a, -1), entretanto ela vale para qualquer número de dimensões. O classificador usado para fazer predições é chamado de função hipótese h: Pontos acima ou no hiperplano serão classificados como classe +1, e pontos abaixo do hiperplano serão classificados como classe -1. Buscando encontrar métricas que não sofram variações de escala e que sejam a melhor métrica de comparação possível, usamos o conceito de margem geométrica: Os problemas de encontrar possíveis valores para ‘w’ e para ‘b’ são chamados de problemas de otimização, o que significa que buscamos o valor máximo de M: SVM para classificação: # Support Vector Machine # Importing the libraries import numpy as np import matplotlib.pyplot as plt import pandas as pd # Importing the datasets datasets = pd.read_csv('Social_Network_Ads.csv') X = datasets.iloc[:, [2,3]].values Y = datasets.iloc[:, 4].values # Splitting the dataset into the Training set and Test set from sklearn.model_selection import train_test_split X_Train, X_Test, Y_Train, Y_Test = train_test_split(X, Y, test_size = 0.25, random_state = 0) # Feature Scaling from sklearn.preprocessing import StandardScaler sc_X = StandardScaler() X_Train = sc_X.fit_transform(X_Train) X_Test = sc_X.transform(X_Test) # Fitting the classifier into the Training set from sklearn.svm import SVC classifier = SVC(kernel = 'linear', random_state = 0) classifier.fit(X_Train, Y_Train) # Predicting the test set results Y_Pred = classifier.predict(X_Test) # Making the Confusion Matrix from sklearn.metrics import confusion_matrix cm = confusion_matrix(Y_Test, Y_Pred) # Visualising the Training set results from matplotlib.colors import ListedColormap X_Set, Y_Set = X_Train, Y_Train X1, X2 = np.meshgrid(np.arange(start = X_Set[:, 0].min() - 1, stop = X_Set[:, 0].max() + 1, step = 0.01), np.arange(start = X_Set[:, 1].min() - 1, stop = X_Set[:, 1].max() + 1, step = 0.01)) plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape), alpha = 0.75, cmap = ListedColormap(('red', 'green'))) plt.xlim(X1.min(), X1.max()) plt.ylim(X2.min(), X2.max()) for i, j in enumerate(np.unique(Y_Set)): plt.scatter(X_Set[Y_Set == j, 0], X_Set[Y_Set == j, 1], c = ListedColormap(('red', 'green'))(i), label = j) plt.title('Support Vector Machine (Training set)') plt.xlabel('Age') plt.ylabel('Estimated Salary') plt.legend() plt.show() # Visualising the Test set results from matplotlib.colors import ListedColormap X_Set, Y_Set = X_Test, Y_Test X1, X2 = np.meshgrid(np.arange(start = X_Set[:, 0].min() - 1, stop = X_Set[:, 0].max() + 1, step = 0.01), np.arange(start = X_Set[:, 1].min() - 1, stop = X_Set[:, 1].max() + 1, step = 0.01)) plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape), alpha = 0.75, cmap = ListedColormap(('red', 'green'))) plt.xlim(X1.min(), X1.max()) plt.ylim(X2.min(), X2.max()) for i, j in enumerate(np.unique(Y_Set)): plt.scatter(X_Set[Y_Set == j, 0], X_Set[Y_Set == j, 1], c = ListedColormap(('red', 'green'))(i), label = j) plt.title('Support Vector Machine (Test set)') plt.xlabel('Age') plt.ylabel('Estimated Salary') plt.legend() plt.show() Naive Bayes Naive Bayes, também chamado de Idiot Bayes, é um algoritmo de classificação para problemas binomiais ou multinomiais. Naive, em inglês, significa inocente e, o motivo dessa nomenclatura se deve à sua premissa: as variáveis de um problema são independentes e de igual importância para o resultado, algo que não acontece com tanta frequência no mundo real. Ele utiliza cálculos probabilísticos baseados no Teorema de Bayes: P(A|B) = P(B|A) x P(A) / P(B) onde P(A) é a probabilidade de A acontecer, P(B) é a probabilidade de B acontecer, P(B|A) é a probabilidade de de B acontecer sendo que A já aconteceu e P(A|B) o inverso. Para demonstrar com maior facilidade o teorema, utilizaremos o seguinte exemplo: ● ● ● ● 1000 maçãs foram colhidas e pesadas 30% das maçãs pesavam menos de 140 gramas 60% das maçãs leves estavam impróprias para o consumo 20% das maçãs pesadas estavam com impróprias para o consumo Com base nessas informações, qual a probabilidade de, caso uma maçã seja imprópria para o consumo, ela pese menos de 140 gramas? Primeiramente, transformaremos as variáveis para os termos da equação P(A) = prob. de ser leve P(B) = prob. de ser imprópria para consumo P(A|B) = prob. de ser leve sendo que é confirmado que é imprópria P(B|A) = prob. de ser imprópria sendo que é confirmado que é leve Assim: P(leve|imprópria) = P(imprópria|leve) x P(leve) / P(imprópria) Sabendo que a probabilidade de de uma maçã ser imprópria para o consumo é a soma das probabilidades de ela ser leve e imprópria e de ser pesada e imprópria, temos: P(leve|imprópria) = 0,6 x 0,3 / (0,6 x 0,3 + 0,2 x 0,7) P(leve|imprópria) = 0,18 / (0,18 + 0,14) P(leve|imprópria) = 0,5625 Logo, a probabilidade de ela ser leve sabendo-se que é imprópria para o consumo é de 56,25%. No modelo, é feita uma tabela de frequências para cada variável e, assim, calculada cada probabilidade. Os jogadores vão jogar (play) se estiver com com sol (sunny)? P(sim|sol) = P(sol|sim) x P(sim) / P(sol) P(sim|sol) = 3/9 x 9/14 / 5/14 P(sim|sol) = 0,599999999 = 60% Vantagens: ● Simples e rápido com bom desempenho; ● Necessita de apenas um pequeno número de dados para uma boa precisão; ● Quando o modelo já é compatível com independência das variáveis, o modelo é melhor do que os de regressão; Desvantagens: ● Modelo raramente representa o mundo real onde todas as variáveis são independentes; ● Caso uma variável não seja observada, a sua frequência será 0 e não será possível realizar as estimativas; Usos: ● Classificação de textos; ● Predições de real time; ● Sistemas de recomendação; Importing the libraries import numpy as np import matplotlib.pyplot as plt import pandas as pd # Importing the dataset dataset = pd.read_csv('Social_Network_Ads.csv') X = dataset.iloc[:, [2, 3]].values y = dataset.iloc[:, -1].values # Splitting the dataset into the Training set and Test set from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20, random_state = 0) # Feature Scaling from sklearn.preprocessing import StandardScaler sc = StandardScaler() X_train = sc.fit_transform(X_train) X_test = sc.transform(X_test) # Training the Naive Bayes model on the Training set from sklearn.naive_bayes import GaussianNB classifier = GaussianNB() classifier.fit(X_train, y_train) # Predicting the Test set results y_pred = classifier.predict(X_test) # Making the Confusion Matrix from sklearn.metrics import confusion_matrix, accuracy_score ac = accuracy_score(y_test,y_pred) cm = confusion_matrix(y_test, y_pred) Random Forest O Random Forest é um modelo de classificação e regressão amplamente utilizado em machine learning devido a sua flexibilidade e facilidade de uso. Em suma, essa técnica utiliza a combinação de várias árvores de decisão para gerar um único resultado. As árvores de decisão, em inglês decision trees, iniciam com uma condição que analisa e “guia” uma um dado ou amostra para um certo grupo, que por sua vez passa também por uma outra condição e, assim, vai filtrando esse dado até chegar a uma conclusão final. Por exemplo: é necessário descobrir se o público irá comprar um certo celular. Então esse celular passa por uma árvore de decisão. A primeira condição é o preço, se for barato o celular será comprado, se for caro, passa para outra condição, e assim sucessivamente. No final, a árvore retorna o valor, neste caso se vai ou não ser comprado. O Random Forest é a junção de diversas árvores de decisão, cada qual com uma amostra de dados diferente. Para ser usada como um algoritmo de classificação, a moda dos resultados de todas as árvores é escolhida, enquanto que, para a regressão, é a média dos resultados. Seguindo o exemplo dos celulares, agora é necessário saber qual modelo de celular as pessoas mais irão gostar. Assim, junta-se todos os modelos de uma empresa e são separados diferentes amostras de forma aleatória contendo um número x de aparelhos. Cada amostra então passa por uma árvore diferente, que gera como resposta um único modelo de celular. Por fim, os resultados são analisados e aquele que tiver maior frequência será declarado como o mais popular. Vantagens: ● Em comparação com árvores de decisão simples, o Random Forest minimiza o risco de sobreajuste; ● Flexibilidade para trabalhar com regressão ou classificação; ● Modelo robusto que aguenta grande volume de dados; Desvantagens: ● Problemas de alta complexidade não funcionam tão bem nesse modelo; ● Ele precisa de um volume de dados maior do que alguns outros modelos; Usos: ● Instituições bancárias para determinar aprovação de crédito; ● Clinicamente para diagnosticar doenças; ● Preferência de produtos em e-commerces; # Importing the libraries import numpy as np import matplotlib.pyplot as plt import pandas as pd # Importing the datasets datasets = pd.read_csv('Social_Network_Ads.csv') X = datasets.iloc[:, [2,3]].values Y = datasets.iloc[:, 4].values # Splitting the dataset into the Training set and Test set from sklearn.model_selection import train_test_split X_Train, X_Test, Y_Train, Y_Test = train_test_split(X, Y, test_size = 0.25, random_state = 0) # Feature Scaling from sklearn.preprocessing import StandardScaler sc_X = StandardScaler() X_Train = sc_X.fit_transform(X_Train) X_Test = sc_X.transform(X_Test) # Fitting the classifier into the Training set from sklearn.ensemble import RandomForestClassifier classifier = RandomForestClassifier(n_estimators = 200, criterion = 'entropy', random_state = 0) classifier.fit(X_Train,Y_Train) # Predicting the test set results Y_Pred = classifier.predict(X_Test) # Making the Confusion Matrix from sklearn.metrics import confusion_matrix cm = confusion_matrix(Y_Test, Y_Pred) # Visualising the Training set results from matplotlib.colors import ListedColormap X_Set, Y_Set = X_Train, Y_Train X1, X2 = np.meshgrid(np.arange(start = X_Set[:, 0].min() - 1, stop = X_Set[:, 0].max() + 1, step = 0.01), np.arange(start = X_Set[:, 1].min() - 1, stop = X_Set[:, 1].max() + 1, step = 0.01)) plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape), alpha = 0.75, cmap = ListedColormap(('red', 'green'))) plt.xlim(X1.min(), X1.max()) plt.ylim(X2.min(), X2.max()) for i, j in enumerate(np.unique(Y_Set)): plt.scatter(X_Set[Y_Set == j, 0], X_Set[Y_Set == j, 1], c = ListedColormap(('red', 'green'))(i), label = j) plt.title('Random Forest Classifier (Training set)') plt.xlabel('Age') plt.ylabel('Estimated Salary') plt.legend() plt.show() # Visualising the Test set results from matplotlib.colors import ListedColormap X_Set, Y_Set = X_Test, Y_Test X1, X2 = np.meshgrid(np.arange(start = X_Set[:, 0].min() - 1, stop = X_Set[:, 0].max() + 1, step = 0.01), np.arange(start = X_Set[:, 1].min() - 1, stop = X_Set[:, 1].max() + 1, step = 0.01)) plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape), alpha = 0.75, cmap = ListedColormap(('red', 'green'))) plt.xlim(X1.min(), X1.max()) plt.ylim(X2.min(), X2.max()) for i, j in enumerate(np.unique(Y_Set)): plt.scatter(X_Set[Y_Set == j, 0], X_Set[Y_Set == j, 1], c = ListedColormap(('red', 'green'))(i), label = j) plt.title('Random Forest Classifier (Test set)') plt.xlabel('Age') plt.ylabel('Estimated Salary') plt.legend() plt.show() # Random Forest Classifier Redes Neurais Artificiais O que são redes neurais? É um método de inteligência artificial que ensina computadores a processar dados se inspirando no cérebro humano. Dentro da machine learning, é um tipo de processo de aprendizado profundo, que usa nós (neurônios) interconectados em estruturas de camadas, tal qual o cérebro humano faz com os neurônios e seus links. Elas criam sistemas adaptativos em que os computadores aprendem com os erros e continuamente corrigem seus processos de solução de problemas, aumentando a precisão de suas respostas. Redes neurais artificiais costumam ser compostas por camadas de um nó, contendo uma camada de entrada, pelo menos uma camada oculta de processamento e uma única camada de saída. As conexões entre neurônios possuem pesos e limites associados, os quais são constantemente corrigidos à medida que o modelo é treinado e aprende como chegar de forma mais eficiente em um resultado suficientemente próximo de um output desejado. Além disso, redes neurais podem ajudar computadores a tomar decisões mais inteligentes com menor assistência humana, tendo em vista que o aprendizado deles em como compreender e relacionar os modelos e dados de entrada/saída de forma complexa e não linear. Assim, são capazes de realizar generalizações e inferências sobre dados não estruturados, ainda que não recebam treinamento explícito para aquelas atividades. Por exemplo, uma rede neural saberia que a Av. Castelo Branco é um lugar e que Castelo Branco era o nome de uma pessoa. Alguns de seus usos: ● Diagnóstico médico feito por classificação de imagem ● Marketing direcionado por filtros de mídias sociais e análise de dados comportamentais ● Previsões financeiras feitas pelo processamento de dados históricos ● Previsões de demandas de energia e carga elétrica ● Processo e controle de qualidade ● Identificação de compostos químicos ● Visão Computacional ● Reconhecimento de voz ● Processamento de linguagem natural ● Mecanismos de recomendação Vantagens e desvantagens das RNA’s: Vantagens: ● Armazena as informações em toda a rede, e não num banco de dados. O sumiço de alguma informação não atrapalha o funcionamento da rede visto que ela já vai ter aprendido com aquele pedaço de dado. ● Consegue trabalhar com conhecimento incompleto, tendo outputs ainda que a performance caia a depender da importância da informação ● Tem tolerância a falhas, a corrupção de neurônios da RNA não trava a geração de outputs. ● Tem memória distribuída. Para que o aprendizado da RNA aconteça, é preciso determinar exemplos para ensinar a rede de acordo com os outputs desejados. O sucesso dela é proporcional às instâncias selecionadas, e a rede pode produzir outputs falsos, caso o evento não possa ser mostrado para a rede em todos seus aspectos. ● A corrupção da rede é gradual, dado que ao longo do tempo ela se torna mais lenta. Ela não para abruptamente. ● Capaz de realizar aprendizado de máquina. ● Capaz de realizar processamento paralelo, fazendo mais de um “trabalho” por vez. Desvantagens: ● Depende de um hardware poderoso, capaz de realizar processamento paralelo, de acordo com a estrutura da RNA. ● Incapacidade de explicar o comportamento da rede ao solucionar um problema. Não há dicas de como ou porque chegou àquela solução. Logo, há menos confiança na rede. ● Alto ciclo de tentativas e erros acontecem tentando chegar na estrutura ideal da rede neural, tomando um tempo considerável. ● Necessidade de tradução do problema para valores numéricos a fim de ser introduzido para a RNA, dependendo, portanto, da habilidade do usuário para o funcionamento do mecanismo. ● Duração da rede desconhecida. A queda dos valores de erros nos testes significa apenas que a etapa de treinamento foi concluída, não dizendo se foram os resultados mais propícios. Modelagem matemática Uma rede neural é definida como uma função multivariável composta por tantas funções quanto camadas existem nesta rede. O domínio e o contradomínio são conjuntos reais da mesma dimensão do input e output, respectivamente. Cada uma dessas funções que compõem a função geral são funções compostas pela combinação linear de um input ‘x’ com um peso ou coeficiente ‘w’, mais um viés ‘b’ (bias), tudo isso multiplicado por um coeficiente ‘a’ chamado de função de ativação, igual em cada função intermediária. O fator de ativação varia de acordo com a natureza do objetivo que se quer atingir. Por exemplo, em regressões, usa-se a função identidade como ativação. Logo, só existe a combinação linear na função. Em tarefas de classificação binárias, o fator de ativação é o sigmóide, uma função cujos valores variam entre 0 e 1. Para facilitar a associação entre o modelo matemático e a estrutura da rede neural: os nós de input são o parâmetro “x” da função f(x), a camada de output representa o próprio valor de f(x), as camadas ocultas são representadas pelas funções intermediárias, e por fim, os links entre os nós, ou sinapses, para continuar a analogia entre o modelo e a organização de um cérebro, são representados pelos pesos “w”. Treinamento de uma ANN para predição da taxa de churn em um banco, criado por u/TatevKaren: #----------------------- Artificial Neural Network for classification --------------------# #importing required libraries import numpy as np import pandas as pd import tensorflow as tf from sklearn.compose import ColumnTransformer from sklearn.preprocessing import OneHotEncoder from from from from sklearn.preprocessing import LabelEncoder sklearn.model_selection import train_test_split sklearn.preprocessing import StandardScaler sklearn.metrics import confusion_matrix, accuracy_score #----------------------- Data Pre-processing ----------------------# # Checking the tensorflow version print(tf.__version__) # Loading the data bank_data = pd.read_csv("Artificial_Neural_Network_Case_Study_data.csv") # Taking all rows and all columns in the data except the last column as X (feature matrix) #the row numbers and customer id's are not necessary for the modelling so we get rid of and start with credit score X = bank_data.iloc[:,3:-1].values print("Independent variables are:", X) #taking all rows but only the last column as Y(dependent variable) y = bank_data.iloc[:, -1].values print("Dependent variable is:", y) # Transforming the gender variable, labels are chosen randomly le = LabelEncoder() X[:,2] = le.fit_transform(X[:,2]) print(X) # Transforming the geography column variable, labels are chosen randomly, the ct asks for argument [1] the index of the target vb ct = ColumnTransformer(transformers = [('encoder', OneHotEncoder(),[1])], remainder = 'passthrough') X = np.array(ct.fit_transform(X)) print(X) # Splitting the data into train and test X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0) #printing the dimensions of each of those snapshots to see amount of rows and columns i each of them print(X_train.shape, X_test.shape) print(y_train.shape, y_test.shape) # Data Scaling/normalization of the features that will go to the NN sc = StandardScaler() X_train = sc.fit_transform(X_train) X_test = sc.transform(X_test) #----------------------- Building the model -----------------------# # Initializing the ANN by calling the Sequential class fromm keras of Tensorflow ann = tf.keras.models.Sequential() #--------------------------------------------------------------------------------# Adding "fully connected" INPUT layer to the Sequential ANN by calling Dense class #--------------------------------------------------------------------------------# Number of Units = 6 and Activation Function = Rectifier ann.add(tf.keras.layers.Dense(units = 6, activation = 'relu')) #--------------------------------------------------------------------------------# Adding "fully connected" SECOND layer to the Sequential AMM by calling Dense class #--------------------------------------------------------------------------------# Number of Units = 6 and Activation Function = Rectifier ann.add(tf.keras.layers.Dense(units = 6, activation = 'relu')) #--------------------------------------------------------------------------------# Adding "fully connected" OUTPUT layer to the Sequential ANN by calling Dense class #--------------------------------------------------------------------------------# Number of Units = 1 and Activation Function = Sigmoid ann.add(tf.keras.layers.Dense(units = 1, activation = 'sigmoid')) #----------------------- Training the model -----------------------# # Compiling the ANN # Type of Optimizer = Adam Optimizer, Loss Function = crossentropy for binary dependent variable, and Optimization is done w.r.t. accuracy ann.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy']) # Training the ANN model on training set (fit method always the same) # batch_size = 32, the default value, number of epochs = 100 ann.fit(X_train, y_train, batch_size = 32, epochs = 100) #----------------------- Evaluating the Model ---------------------# # the goal is to use this ANN model to predict the probability of the customer leaving the bank # Predicting the churn probability for single observations #Geography: French #Credit Score:600 #Gender: Male #Age: 40 years old #Tenure: 3 years #Balance: $60000 #Number of Products: 2 #with Credit Card #Active member #Estimated Salary: $50000 print(ann.predict(sc.transform([[1, 0, 0, 600, 1, 40, 3, 60000, 2, 1, 1, 50000]]))) print(ann.predict(sc.transform([[1, 0, 0, 600, 1, 40, 3, 60000, 2, 1, 1, 50000]])) > 0.5) # this customer has 4% chance to leave the bank #show the vector of predictions and real values #probabilities y_pred_prob = ann.predict(X_test) #probabilities to binary y_pred = (y_pred_prob > 0.5) print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)), 1)) #Confusion Matrix confusion_matrix = confusion_matrix(y_test, y_pred) print("Confusion Matrix", confusion_matrix) print("Accuracy Score", accuracy_score(y_test, y_pred)) Modelos Não Supervisionado Redes Neurais Profundas Redes neurais profundas, assim como as redes neurais artificiais, usam de camadas e mais camadas de neurônios matemáticos para processar fala humana e para visão computacional, que é a classificação e reconhecimento de objetos em imagens ou vídeos. Como é dividida em camadas, a primeira recebe o nome de Input Layer (camada de input), as camadas intermediárias de processamento são chamadas de Hidden Layers (camadas ocultas), e a última camada se chama Output Layer (camada de saída). Cada uma dessas camadas contém um tipo simples de função de ativação. Conceito As redes neurais profundas ( também chamadas de DNN) fazem parte da grande família de algoritmos e métodos de Machine Learning (aprendizado de máquina) baseado em redes neurais artificiais com feature learning. Esse aprendizado pode ser supervisionado ou não, sendo nesse caso não supervisionado, o que permite que o modelo aprenda padrões enquanto observa os casos de treinamento. As redes neurais que abordaremos aqui serão as redes neurais convolucionais (também chamada de CNN), um tipo de DNN bastante utilizado. Por ser uma rede neural, seu funcionamento visa imitar o aprendizado do cérebro humano. Alguns dos usos mais comuns de CNN são para reconhecimento ou classificação de objetos em imagens. São exemplos práticos disso: programas que interpretam caligrafia ou documentos impressos, o algoritmo de reconhecimento de rostos no Google Fotos (pasta “Pessoas”, “Paisagens” etc), o ensino de um sistema de navegação de carros sem motoristas que precisam diferenciar pessoas, objetos e outros carros com alta precisão a fim de não colocar vidas em perigo. O funcionamento de redes neurais profundas é idêntico ao das redes neurais artificiais, contendo apenas mais hidden layers, tendo em vista que uma rede neural artificial geralmente contém até 3 camadas. Algumas CNN 's chegam a ter até 100 camadas. Classes de problemas e usos São aplicações das redes neurais convolucionais o reconhecimento de imagens e vídeos, classificação e segmentação de imagem, sistemas de recomendação, processamento de linguagem natural, interfaces cérebro-máquina, análise de imagens médicas e no mercado financeiro. A CNN realmente brilha quando o problema é classificação de imagens, por ter um pré-processamento relativamente baixo comparado a outros algoritmos. Como ele aprende a otimizar os filtros que compõem as imagens pelo aprendizado automatizado, ao invés de usar a famigerada “mão na massa”, funcionando como uma grande vantagem. Definição teórica e modelagem matemática Redes neurais convolucionais são tipos especialistas de redes neurais que utilizam a convolução como operação matemática ao processar os sinais de input ao invés de compactar todas as entradas num vetor ou matriz para realização dos cálculos necessários. Esse processo faz uso de uma janela de análise dos pixels da imagem de entrada, chamado também de kernel. A imagem é formada por vários filtros (pense em uma imagem colorida por RGB, ela conteria 3 filtros da mesma imagem em cada uma das cores principais). O kernel desliza sobre a imagem, aplicando a função convolução nos pixels da imagem, passando apenas pelo seu espaço de análise. Após a convolução, o dado processado passa para outra camada, que pode ser uma camada de pooling, conexão total, não linearidade, tudo para classificar melhor o objeto. O pooling funciona como redutor das dimensões da imagem por combinar os outputs de vários neurônios em um único nó. Essas aglomerações ou clusters podem ser máximos ou médios, usando os valores máximos locais dos neurônios ou os valores médios locais dos neurônios para realizar a aglomeração. A conexão total é a conexão de cada nó de uma camada a cada nó de uma outra camada. Ajuda na classificação da imagem. Já a não linearidade são camadas de operação da CNN que aproximam funções não lineares ou consegue prever a classe de uma função que não segue um processo de decisão linear. Essa camada visa deixar a CNN mais robusta, sendo capaz de realizar Data Augmentation, pela definição de uma matriz peso extra. Data Augmentation é nada mais que o processo de aumentar a quantidade e a diversidade dos dados do dataset. Pensando numa imagem de uma cadeira, a data augmentation seriam imagens editadas dessa mesma cadeira, por diferentes vistas, cores, iluminação, distorção, e até diferentes tipos de cadeira. Vantagens e Desvantagens As vantagens deste algoritmo são os resultados precisos que ele proporciona, podendo ser para modelos preditivos, interpretação de imagens e textos. Desvantagens são a complexidade de treinamento do modelo, ainda mais com a maior quantidade de camadas, a possibilidade de overfitting do modelo, a análise de seus neurônios não dá um entendimento de como o aprendizado e análise são feitos. Uso do dataset MNIST para reconhecimento de dígitos escritos à mão: #importações dos pacotes necessarios import tensorflow as tf from tensorflow.keras import layers,models from tensorflow import keras import numpy as np #carrega o dataset (X_train, y_train) , (X_test, y_test) = keras.datasets.mnist.load_data() #Scaling e teste do dataset X_train = X_train / 255 X_test = X_test / 255 X_train[0] X_train.shape X_train = X_train.reshape(-1,28,28,1) X_train.shape X_test = X_test.reshape(-1,28,28,1) X_test.shape #Criando e treinando a CNN convolutional_neural_network = models.Sequential([ layers.Conv2D(filters=25, kernel_size=(3, 3), activation='relu', input_shape=(28,28,1)), layers.MaxPooling2D((2, 2)), layers.Conv2D(filters=64, kernel_size=(3, 3), activation='relu'), layers.MaxPooling2D((2, 2)), layers.Conv2D(filters=64, kernel_size=(3, 3), activation='relu'), layers.MaxPooling2D((2, 2)), layers.Flatten(), layers.Dense(64, activation='relu'), layers.Dense(10, activation='softmax') ]) convolutional_neural_network.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) convolutional_neural_network.fit(X_train, y_train, epochs=10) #avaliando o modelo CNN convolutional_neural_network.evaluate(X_test, y_test) #Fazendo predicoes y_predicted_by_model = convolutional_neural_network.predict(X_test) y_predicted_by_model[0] #getting probability score for each class digits np.argmax(y_predicted[0]) y_predicted_labels = [np.argmax(i) for i in y_predicted] y_predicted_labels[:5] Processamento de linguagem natural Algoritmos de processamento de linguagem natural (NLP) visam construir máquinas que entendam e respondam por texto ou voz da mesma forma que um humano responderia. É uma área bastante desafiadora, considerando as quase infinitas possibilidades de formação de um discurso escrito ou falado, além dos regionalismos, divergências entre os indivíduos falantes, emoções, ironias, dentre outros. Conceito É um ramo da inteligência artificial que permite às máquinas compreender, manipular e até gerar linguagem humana. Ferramentas desse tipo já estão entranhadas na sociedade, como o corretor ortográfico automático do celular, as assistentes virtuais, os chatbox, filtros de spam no email, tradução automática de legendas no YouTube. A compreensão de texto buscada com o NLP é a capacidade de reconhecer o contexto, realizar análise sintática, léxica, morfológica e semântica, criar resumos, realizar varreduras atrás de pedaços de informações, interpretar e analisar sentimentos e até aprender conceitos advindos dos textos processados. Usos São usos da NLP: traduções de textos, sites, legendas automáticas de vídeos, tradução simultânea, interpretação de sentimentos. Outros usos interessantes são a otimização de mecanismos de pesquisa, a análise de mídias sociais, moderação de conteúdos, agilizar o processo de organização de documentos relevantes ao meio jurídico, seguradoras. Definição teórica e modelagem matemática Ferramentas NLP funcionam com a vetorização do texto, transformando o texto em algo que a máquina consiga compreender. Um dos usos mais populares é a análise de sentimento do texto. Se quisermos usar a análise de sentimentos em tweets, por exemplo, como seria feito? Existe uma api oficial do Twitter que nos permite coletar dados para construir nosso dataset de textos de tweets. Alguns processamentos já podem ser feitos com os dados crus, como a extração de emoções em hashtags (#sadreacts), tradução de emojis em texto puro ( :thumbs_up: é a representação escrita do emoji do joinha). Com isso, conseguimos mapear os termos e obter algumas indicações de emoções nas quais eles se encaixam. Com isso, passamos para o pré-processamento dos textos. Para isso, há a remoção de caracteres especiais, de repetições e de stop words, e a conversão de caracteres maiúsculos para minúsculo. Após esse tratamento inicial dos dados, transformamos o texto em vetores para que o modelo de aprendizado consiga trabalhar. Para os dados de saída, usando um classificador binário, quanto mais positivo um tweet, maior será seu resultado, e por dedução, quanto mais negativo, menor (mais próximo de zero) será o resultado. Usando a relação entre termos e sentimentos e os resultados dados pelo modelo classificador, conseguimos metrificar a emoção passada pelo tweet que usa um emoji triste e o tweet que tem a hashtag #sadreacts. Com esse dataset, conseguimos construir um modelo capaz de descobrir a emoção mais presente num texto, ferramenta que teria grande uso nas agências de marketing. Vantagens e Desvantagens As vantagens deste algoritmo são o aprendizado constante e a grande quantidade de dados que podem ser processados de uma vez. E uma das desvantagens é a falta de otimização da interpretação de formas mais complexas de comunicação, como gírias, ambiguidades, ironias etc. Pré-processamento do texto: import re import nltk from time import time from emoji import demojize def preprocess(texts, quiet=False): start = time() # Lowercasing texts = texts.str.lower() # Remove special chars texts = texts.str.replace(r"(http|@)\S+", "") texts = texts.apply(demojize) texts = texts.str.replace(r"::", ": :") texts = texts.str.replace(r"'", "'") texts = texts.str.replace(r"[^a-z\':_]", " ") # Remove repetitions pattern = re.compile(r"(.)\1{2,}", re.DOTALL) texts = texts.str.replace(pattern, r"\1") # Transform short negation form texts = texts.str.replace(r"(can't|cannot)", 'can not') texts = texts.str.replace(r"n't", ' not') # Remove stop words stopwords = nltk.corpus.stopwords.words('english') stopwords.remove('not') stopwords.remove('nor') stopwords.remove('no') texts = texts.apply( lambda x: ' '.join([word for word in x.split() if word not in stopwords]) ) if not quiet: print("Time to clean up: {:.2f} sec".format(time() - start)) return texts Tokenização: import pickle from tensorflow.keras.preprocessing.text import Tokenizer num_words = 10000 tokenizer = Tokenizer(num_words=num_words, lower=True) tokenizer.fit_on_texts(dataset.cleaned_data.text) file_to_save = Path('../datasets/sentiment_analysis/tokenizer.pickle').resolve() with file_to_save.open('wb') as file: pickle.dump(tokenizer, file) Modelo LSTM e CNN: from from from from tensorflow.keras.layers tensorflow.keras.layers tensorflow.keras.layers tensorflow.keras.models import import import import Input, Embedding, SpatialDropout1D, LSTM GlobalAveragePooling1D, GlobalMaxPooling1D Bidirectional, Conv1D, Dense, concatenate Model input_dim = min(tokenizer.num_words, len(tokenizer.word_index) + 1) num_classes = len(data.label.unique()) embedding_dim = 500 input_length = 100 lstm_units = 128 lstm_dropout = 0.1 recurrent_dropout = 0.1 spatial_dropout=0.2 filters=64 kernel_size=3 input_layer = Input(shape=(input_length,)) output_layer = Embedding( input_dim=input_dim, output_dim=embedding_dim, input_shape=(input_length,) )(input_layer) output_layer = SpatialDropout1D(spatial_dropout)(output_layer) output_layer = Bidirectional( LSTM(lstm_units, return_sequences=True, dropout=lstm_dropout, recurrent_dropout=recurrent_dropout) )(output_layer) output_layer = Conv1D(filters, kernel_size=kernel_size, padding='valid', kernel_initializer='glorot_uniform')(output_layer) avg_pool = GlobalAveragePooling1D()(output_layer) max_pool = GlobalMaxPooling1D()(output_layer) output_layer = concatenate([avg_pool, max_pool]) output_layer = Dense(num_classes, activation='softmax')(output_layer) model = Model(input_layer, output_layer) Análise de Score de Sentimento data_dict = {} query_dict = { 'query': [], 'mean': [], 'max': [], 'min': [], 'std': [], 'count': [], 'emotion': [] } dir_files = os.listdir(dataset_dir) with tqdm(total=len(dir_files)) as t: for filename in dir_files: dataset = pd.read_csv(os.path.join(dataset_dir, filename)) cleaned_texts = preprocess(dataset.text, quiet=True) query = re.findall(r'(#[^.]+|:.+:)', filename)[0] predict_sequences = [text.split() for text in cleaned_texts] list_tokenized_predict = tokenizer.texts_to_sequences(predict_sequences) x_predict = pad_sequences(list_tokenized_predict, maxlen=100) result = model.predict(x_predict) emotion = relations[query] query_dict['query'].append(query) query_dict['mean'].append(np.mean(result)) query_dict['max'].append(np.amax(result)) query_dict['min'].append(np.amin(result)) query_dict['count'].append(len(dataset)) query_dict['std'].append(np.std(result)) query_dict['emotion'].append(emotion) if emotion in data_dict: data_dict[emotion] = np.concatenate([data_dict[emotion], result]) else: data_dict[emotion] = result t.update() Impressão da relação entre os sentimentos df = pd.DataFrame(data=query_dict) for emotion in df.emotion.unique(): display(df[df.emotion == emotion]) emotion_dict = { 'emotion': [], 'mean': [], 'max': [], 'min': [], 'std': [], 'count': [] } for emotion, result in data_dict.items(): emotion_dict['emotion'].append(emotion) emotion_dict['mean'].append(np.mean(result)) emotion_dict['max'].append(np.amax(result)) emotion_dict['min'].append(np.amin(result)) emotion_dict['std'].append(np.std(result)) emotion_dict['count'].append(len(result)) emotion_df = pd.DataFrame(data=emotion_dict) display(emotion_df) K-Means Clustering O K-Means Clustering é um algoritmo não supervisionado utilizado para problemas de classificação. Ele funciona à base de clusterização, ou seja, agrupamento de dados semelhantes entre si. Em suma, primeiramente é escolhido uma quantidade K de clusters, e depois K pontos aleatórios para servirem como centróides. Após cálculos matemáticos, o centróide é ajustado até que a média das distâncias entre o centróide e os outros pontos de seu cluster se tornem constantes. Demonstrando um exemplo matemático, temos o seguinte grupo: S = {3, 4, 5, 11, 12, 13, 21, 26, 31}. Queremos dividir esse grupo em dois clusters, ou seja, K = 2. Os seguintes passos são tomados: 1. São escolhidos aleatoriamente K = 2 valores para serem os centróides dos clusters. Selecionamos 5 e 13 para serem esses valores, e os denominaremos de C1 = 5 e C2 = 13; 2. Agrupamos os valores mais próximos de cada centróide de acordo com a distância euclidiana em dois clusters: G1 = {3, 4, 5} em relação a C1 e G2 = {11, 12, 13, 21, 26, 31} em relação a C2; 3. Após isso, é feita uma média de G1 e G2: M1 = 11 + 12 + 13 + 21 + 26 + 31 6 3+4+5 3 = 4 e M2 = = 19; 4. São refeitos os clusters, agora utilizando M1 e M2 como novos centróides: G1 = {3, 4, 5, 11} e G2 = {12, 13, 21, 26, 31} 5. Repete-se o passo 3 para encontrar novas médias (nesse caso, os valores são arredondados): M1 = 3 + 4 + 5 + 11 4 = 5,75 = 6 e M2 = 12 + 13 + 21 + 26 + 31 5 = 20,6 = 21; 6. Continua-se o processo, repetindo as etapas 4 e 5 até que M1 e M2 se tornem constantes: G1 = {3, 4, 5, 11, 12, 13} e G2 = {21, 26, 31}; 7. M1 = 3 + 4 + 5 + 11 + 12 + 13 6 = 8 e M2 = 21 + 26 + 31 3 = 26; 8. G1 = {3, 4, 5, 11, 12, 13} e G2 = {21, 26, 31}; 9. A partir deste ponto, como as médias não serão alteradas, os grupos também não serão alterados, o que resulta em dois clusters bem definidos; Vantagens: ● Facilidade na implementação do código; ● Consegue lidar com uma grande quantidade de dados por ter baixa complexidade; ● Facilmente adaptável para outros exemplos; Desvantagens: ● Não lida bem com clusters de formatos variados; ● Não lida bem com ruídos, já que todos os pontos entram dentro de um cluster; ● O número de clusters deve ser definido previamente; Casos de uso: ● Identificação de locais de crime; ● Detecção de fraudes; ● Classificação de documentos; import import import import import import numpy as np random matplotlib.pyplot as plt copy os imageio class KMeans: def __init__(self, n_cluster=3, random_state=721): self.n_cluster = n_cluster self.random_state = random_state def fit(self, dataset): self.X = dataset.iloc[:, [0, 1]] # not use feature labels self.m = self.X.shape[0] # number of training examples self.n = self.X.shape[1] # number of features. initial_centroids = self.initialize_centroids() self.plot_initial_centroids(initial_centroids) self.clustering(initial_centroids) def initialize_centroids(self): initial_centroids = [] random.seed(self.random_state) for i in range(self.n_cluster): initial_centroids.append(np.ravel(self.X.iloc[(random.randint(0, self.m - 1)), :])) return np.array(initial_centroids) def plot_initial_centroids(self, initial_centroids): plt.scatter(self.X.iloc[:,0], self.X.iloc[:,1], c='#000000', s=7, label='Data Points') plt.scatter(initial_centroids[:, 0], initial_centroids[:, 1], marker='*', s=120, c='r', label='Initial Centroids') plt.title('Initial Random Cluster Centers') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend() plt.draw() def clustering(self, centroids): old_centroids = np.zeros(centroids.shape) stopping_criteria = 0.0001 self.iterating_count = 0 self.objective_func_values = [] while self.euclidean_distance(old_centroids, centroids) > stopping_criteria: clusters = np.zeros(len(self.X)) # Assigning each value to its closest cluster for i in range(self.m): distances = [] for j in range(len(centroids)): distances.append(self.euclidean_distance(self.X.iloc[i, :], centroids[j])) cluster = np.argmin(distances) clusters[i] = cluster # Storing the old centroid values to compare centroid moves old_centroids = copy.deepcopy(centroids) # Finding the new centroids for i in range(self.n_cluster): points = [self.X.iloc[j, :] for j in range(len(self.X)) if clusters[j] == i] centroids[i] = np.mean(points, axis=0) # calculate objective function value for current cluster centroids self.objective_func_values.append([self.iterating_count, self.objective_func_calculate(clusters, centroids)]) self.plot_centroids(centroids, clusters) self.iterating_count += 1 self.plot_objective_function_values() def plot_centroids(self, centroids, clusters): colors = ["#4C72B0", "#DD8452", "#55A868", "#C44E52", "#8172B3", "#937860", "#DA8BC3", "#8C8C8C", "#CCB974", "#64B5CD"] fig, ax = plt.subplots() for i in range(self.n_cluster): points = np.array([self.X.iloc[j, :] for j in range(len(self.X)) if clusters[j] == i]) ax.scatter(points[:, 0], points[:, 1], s=7, c=colors[i], label='Cluster {}'.format(i + 1)) ax.scatter(centroids[:, 0], centroids[:, 1], marker='*', s=120, c='#000000', label='Centroids') plt.title('k-Means Clustering\n( Iteration count = {} Objective Function value = {:.2f} )' .format((self.iterating_count + 1), np.array(self.objective_func_values)[self.iterating_count, 1])) plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend() plt.draw() def euclidean_distance(self, a, b): return np.sqrt(np.sum((np.array(a) - np.array(b))**2)) def objective_func_calculate(self, clusters, centroids): """Calculate objective function value for current centroids""" # Calculate objective function value distances_from_centroids = [] for i in range(self.n_cluster): points = np.array([self.X.iloc[j, :] for j in range(len(self.X)) if clusters[j] == i]) for k in range(len(points)): distances_from_centroids.append(self.euclidean_distance(points[k, :], centroids[i])) return sum(distances_from_centroids) def plot_objective_function_values(self): """This function plot graph of objective function value for each iteration """ plt.figure() plt.plot((np.array(self.objective_func_values)[:, 0] + 1), np.array(self.objective_func_values)[:, 1], 'bo') plt.plot((np.array(self.objective_func_values)[:, 0] + 1), np.array(self.objective_func_values)[:, 1], 'k') plt.title('Objective Function') plt.xlabel('Iteration Number') plt.ylabel('Objective Function Value') plt.draw() def save_figures(self, path): """Save all figures plotted with matplotlib to path directory""" # create folder for png files if not os.path.isdir(path): os.makedirs(path) # plt.get_fignums returns a list of existing figure numbers. # then we save all existing figures for i in plt.get_fignums(): plt.figure(i) plt.savefig(os.path.join(path, "figure_{}.png".format(i)), format='png') # close all figure to clear figure numbers plt.close("all") print("Figures for the dataset saved in {}".format(path)) def create_gif(self,path): """Scan path folder, create list from file names in folder, sort these png file names in list, add each png file to images list and create animation from these png files in images list """ # create folder for gif file if not os.path.isdir(os.path.join(path, "animation")): os.makedirs(os.path.join(path, "animation")) png_dir = path images = [] file_names = [] # add each png file name in path file_names list for file_name in os.listdir(png_dir): if file_name.endswith('.png'): file_names.append(file_name) # sort file names according to last digits sorted_file_names = sorted(file_names, key=lambda y: int((y.split('_')[1]).split('.')[0])) # add each file in sorted file names to images list for i in range(len(sorted_file_names)): file_path = os.path.join(png_dir, sorted_file_names[i]) images.append(imageio.imread(file_path)) # remove last png file (objective function figure) from images list images.pop() # save gif file to animation folder imageio.mimsave(os.path.join(path,'animation/animation.gif'), images, duration=0.5) print("Animation of figures saved in {} directory.".format(os.path.join(path,'animation'))) Agglomerative Clustering O Clustering Aglomerativo é um algoritmo de machine learning do tipo não supervisionado utilizado para problemas de classificação. Ele funciona construindo uma hierarquia entre clusters, de forma a criar uma árvore, que pode ser representada por um dendograma. Esse algoritmo também é conhecido como AGNES (Agglomerative Nesting), e é um dos dois tipos de clustering hierárquico, sendo o outro o Divisive Clustering, que funciona de maneira oposta ao agglomerative. Esse método inicia assumindo que cada dado é um cluster próprio. Sucessivamente ele compara os clusters entre si e aglomera os que são mais parecidos, até que todos os dados estejam sob um único cluster. Essa estrutura pode ser visualizada como uma árvore na qual os dados são as folhas, os nós são clusters de tamanhos variados e o cluster geral (dataset completo) é a raiz. Os clusters podem ser obtidos ao cortar essa árvore em qualquer altura. Inicialmente, é feito o preparo dos dados, colocando eles em uma matriz onde as linhas representam indivíduos, e as colunas sendo as variáveis, similar a um banco de dados. Após isso, é feita a comparação de cada dado. Pode ser utilizado, por exemplo, a distância euclidiana: Utilizando a fórmula, encontramos uma distância d entre cada dado, e podemos criar uma matriz na qual o elemento aij representa a distância euclidiana entre os elementos i e j. Depois da matriz, o algoritmo associa e aglomera os indivíduos com a menor distância entre eles em um único cluster. Esse processo acontece repetidamente até que os últimos dois clusters se juntam em um que contém todos os dados. Para a classificação dos grupos, o algoritmo coleta os clusters formados entre a primeira e a última execução, tendo assim, por exemplo, N grupos separados por semelhança entre os elementos contidos neles. Vantagens: ● Facilidade na implementação e na utilização; ● A quantidade de clusters depende apenas de onde a árvore é cortada, não sendo necessário definir a quantidade; Desvantagens: ● Não é possível voltar no algoritmo, caso um elemento seja designado para um cluster, ele é imutável; ● É um algoritmo de alta complexidade, com pelo menos O(n2logn); ● Sensível a ruídos, já que eles também entram na hierarquia; Usos: ● Árvores genéticas e de evoluções; ● Encontrar a origem de uma doença; ● Comparar grupos de pessoas com base em características; import numpy as np import weakref from collections import defaultdict from data_reader import DataReader from similarity import computeDistance from time import time import math from pathlib import Path import pickle import pprint from matplotlib import pyplot as plt from scipy.cluster.hierarchy import dendrogram import sys # Pretty Printer Object for printing with indenting pp = pprint.PrettyPrinter(indent = 4) # decorator fuction for calculating runtime def timer(func): def wrapper(*args): t = time() res=func(*args) print('Finished in', time()-t, 'sec') return res return wrapper # Class for keeping track of Union Operations and updating the linkage matrix class UnionTracker: factor = 1 def __init__(self, points): '''initializer function for UnionTracker''' self.points = points assert self.points > 1, "Number of points should be greater than 0, {} was provided".format(self.points) self.linkage_matrix = np.zeros((self.points-1, 4)) def union(self, A, B, dist, pts, iteration): '''Create a Union Entry in the linkage Matrix''' self.linkage_matrix[iteration][0] = A self.linkage_matrix[iteration][1] = B self.linkage_matrix[iteration][2] = dist*UnionTracker.factor self.linkage_matrix[iteration][3] = pts class Cluster: ''' Cluster class for creating and maintaining the clusters for the heirarchical clustering''' ClusterCount = 0 _instances = defaultdict() maxClusters = 0 def __init__(self, key=None, seq=None): ''' Initialization of clusters ''' self._id = Cluster.ClusterCount self.initID = self._id Cluster.ClusterCount+=1 Cluster.maxClusters += 1 self.clusterMembers = dict() self._instances[self.initID] = weakref.ref(self) self.factor = 1 if key is not None: self.addMember(key, seq) def __del__(self): ''' Destructor for the Cluster Object''' Cluster._instances.pop(self.initID, None) def incrementFactor(self, factor=1): ''' Increment Multiplication Factor''' self.factor += factor def addMember(self, key, seq): ''' Add memebers (data points) to the cluster, in form of dictionary where the dna sequence is the value''' self.clusterMembers[key] = seq def destroy(self): ''' Explcit destructor call ''' self.__del__() def updateID(self, iteration): ''' Update the id of the cluster after merge operation to n+i where i is the iteration number''' self._id = Cluster.maxClusters + iteration @property def memberCount(self): '''returns the number of members in the cluster currently ''' return len(self.clusterMembers.keys()) @property def sequences(self): ''' Returns the Member DNA sequences in the cluster ''' return [self.clusterMembers[key] for key in self.clusterMembers.keys()] @classmethod def getClusterById(cls, clusterID): ''' Fetch cluster object by referencing its id from the Cluster weak reference storage ''' ref = cls._instances[clusterID] obj = ref() return obj @classmethod def generateInitialDistanceMatrix(cls, test = False): ''' Generate the initial nxn distance matrix by computing Distance between each DNA sequence ''' # For Testing Purpose if test == True: cls.simMatrix = np.array([[0,9,3,6,11],[9,0,7,5,10],[3,7,0,9,2],[6,5,9,0,8],[11,10,2,8,0 ]], dtype=float) # Actual Dataset Implementation else: pickleFilePath = Path('data/simMat_3.pkl') if pickleFilePath.exists(): # Load Pickle File storing the simMatrix with open(pickleFilePath, 'rb') as file: cls.simMatrix = pickle.load(file) else: # Compute Distance among DNA Sequence cls.simMatrix = np.ones((cls.ClusterCount, cls.ClusterCount)) for cID in range(cls.ClusterCount): clusterA = cls.getClusterById(cID) for _cID in range(cID, cls.ClusterCount): clusterB = cls.getClusterById(_cID) seq1 = clusterA.sequences[0] seq2 = clusterB.sequences[0] similarity_1 = computeDistance(seq1, seq2) cls.simMatrix[cID, _cID] = similarity_1 cls.simMatrix[_cID, cID] = similarity_1 print("similarity between {} and {} = {}\r".format(cID, _cID, similarity_1), end='', flush=True) sys.stdout.flush() print('') # Save The Pickle File with open(pickleFilePath, 'wb') as file: pickle.dump(cls.simMatrix, file) # Normalize the Matrix # minval = np.amin(cls.simMatrix, axis=(0,1)) # maxval = np.amax(cls.simMatrix, axis=(0,1)) # cls.simMatrix = ((cls.simMatrix-minval)/(maxval-minval)) @classmethod def getClusters(cls): for key in cls._instances.keys(): ref = cls._instances[key] obj = ref() if obj is not None: yield obj else: cls._instances.pop(key, None) @classmethod def currentClusterCount(cls): ''' Returns the currently existent clusters''' return len(cls._instances.keys()) # Class method to find the minimum distance cluster pair @classmethod def findMinDistance(cls): ''' Find the clusters most similar to each other i.e. with the least distance among them ''' minDistance = 1*math.inf clusterA = None clusterB = None for rowNumber in range(0,cls.simMatrix.shape[0]-1): for colNumber in range(rowNumber+1, cls.simMatrix.shape[1]): if cls.simMatrix[rowNumber, colNumber] < minDistance: minDistance = cls.simMatrix[rowNumber, colNumber] clusterA = rowNumber clusterB = colNumber return clusterA, clusterB, minDistance @classmethod def mergeSimilarClusters(cls, mergedRC, toDelete, iteration, dist, heuristic = 'Centroid'): ''' Cluster merging and new CLuster Creation based on the preset Heuristic Available Heuristic Values are, - Centroid - Max - Min ''' outdated_m = mergedRC outdated_d = toDelete delCluster = cls.getClusterById(outdated_d) toDelete = delCluster._id mergedCluster = cls.getClusterById(outdated_m) mergedRC = mergedCluster._id d_mem = delCluster.memberCount m_mem = mergedCluster.memberCount if heuristic == 'Centroid': #Compute using Centroid Calculation rowSum = (cls.simMatrix[outdated_m, :]*m_mem + cls.simMatrix[outdated_d, :]*d_mem)/(m_mem+d_mem) elif heuristic == 'Max': #Compute using Max Calculation rowStack = np.vstack((cls.simMatrix[outdated_m, :], cls.simMatrix[outdated_d, :])) rowSum = np.amax(rowStack, axis=0) elif heuristic == 'Min': #Compute using Min Calculation rowStack = np.vstack((cls.simMatrix[outdated_m, :], cls.simMatrix[outdated_d, :])) rowSum = np.amin(rowStack, axis=0) #Update the new row cls.simMatrix[:, outdated_m] cls.simMatrix[outdated_m, :] cls.simMatrix[:, outdated_d] cls.simMatrix[outdated_d, :] = = = = rowSum rowSum 1*math.inf 1*math.inf #Merge Data Points into the cluster for key in delCluster.clusterMembers.keys(): mergedCluster.addMember(key, delCluster.clusterMembers[key]) mergedCluster.updateID(iteration) mergedCluster.incrementFactor() print('Union ({} - {}), distance {}'.format(toDelete, mergedRC, dist)) # Delete the redundant clusters explicityly delCluster.destroy() return mergedRC, toDelete, mergedCluster.memberCount, mergedCluster.factor # Driver Function to execute the Heirarchical Clustering @timer def main(): test = False heuristic = 'Centroid' reader = DataReader() data = reader.loadData() dataArray = reader.getDataArray() if test == True: clusters = [Cluster(dataPoint, data[dataPoint]) for dataPoint in list(data.keys())[:5]] else: clusters = [Cluster(dataPoint, data[dataPoint]) for dataPoint in list(data.keys())[:]] Cluster.generateInitialDistanceMatrix(test) Uni = UnionTracker(len(clusters)) print('') iteration = 0 while(Cluster.currentClusterCount() > 1): clsA, clsB, dist = Cluster.findMinDistance() mergedRC = min(clsA, clsB) toDelete = max(clsA, clsB) newIDm, newIDd, pts, factor = Cluster.mergeSimilarClusters(mergedRC, toDelete, iteration, dist, heuristic=heuristic) Uni.union(newIDd, newIDm, dist, pts, iteration) iteration += 1 labels = list(data.keys()) drawDendrogram(Uni, labels, heuristic) def drawDendrogram(UniObj, labels, heuristic): ''' Generate the dendrogram using te UniObject's linkage matrix ''' plt.title("Dendrogram - Agglomerative Clustering -" + heuristic) dendrogram(UniObj.linkage_matrix, show_leaf_counts = True, show_contracted = True, labels = labels) plt.show() if __name__ == "__main__": main() DBSCAN DBSCAN, sigla para Density-based spatial clustering of applications with noise (Clusterização Espacial Baseada em Densidade de Aplicações com Ruído) é um algoritmo não supervisionado de classificação baseado em separar clusters de acordo com a densidade dos dados. Ele foi proposto por Martin Ester, Hans-Peter Kriegel, Jörg Sander e Xiaowei Xu em 1996. O DBSCAN funciona ao agrupar pontos que estão próximos uns aos outros em um mesmo cluster. Não é definido uma quantidade de clusters nem há uma quantidade limite para eles. Existem dois parâmetros que devem ser analisados para definir um cluster: 1. ε (epislon ou eps): A distância entre o ponto de origem e o ponto a ser analisado; 2. MinPts: A quantidade mínima de pontos ao redor do ponto original; Qualquer ponto que não esteja próximo a outro é considerado um ruído (outlier). Para que o algoritmo funcione, é necessário fazer uma boa estimativa dos parâmetros. Para o MinPts, devem ser analisados alguns fatores do dataset, como o tamanho, quantidade de ruído, etc: ● ● ● Quanto maior o dataset, maior deverá ser o MinPts; Quanto mais ruído, maior deverá ser o MinPts; De modo geral, esse parâmetro deve ser igual ou maior que a quantidade de dimensões do dataset, sendo MinPts = 4 para 2 dimensões, e MinPts = 2 x número de dimensões para 3 ou mais dimensões; Para a distância, normalmente valores de tamanho reduzido são preferíveis, porém ele deve ser escolhido com base na distância dos dados. Um método para encontrar o parâmetro é calcular a média da distância entre um ponto e uma quantidade k de vizinhos, no qual k = MinPts. Essas médias são então plotadas em um gráfico de k-distância em ordem crescente, como na figura abaixo. O valor para eps será onde há a maior curvatura. Vantagens: ● Não necessita especificar o número de clusters antes da análise do dataset; ● Ideal para formas arbitrárias de clusters; ● Consegue detectar ruídos no dataset, de modo que o resultado não é influenciado por eles; Desvantagens: ● Em certos casos, não é muito fácil determinar o eps, sendo necessário conhecimento do método e uma análise mais profunda do dataset; ● Caso os clusters possuam densidades diferentes, o DBSCAN terá dificuldades em categorizar os clusters, podendo sinalizar ruídos falsos ou aglomerar ruídos dentro de um cluster; Usos: ● Pode ser utilizado em qualquer área, como biologia, marketing, sistemas de gerenciamento, arqueologia, medicina… ● Utilizado para separar dados em diversos grupos, como tipos de cliente, sintomas de doenças, recomendações de produtos; import numpy as np import math UNCLASSIFIED = False NOISE = None def _dist(p,q): return math.sqrt(np.power(p-q,2).sum()) def _eps_neighborhood(p,q,eps): return _dist(p,q) < eps def _region_query(m, point_id, eps): n_points = m.shape[1] seeds = [] for i in range(0, n_points): if _eps_neighborhood(m[:,point_id], m[:,i], eps): seeds.append(i) return seeds def _expand_cluster(m, classifications, point_id, cluster_id, eps, min_points): seeds = _region_query(m, point_id, eps) if len(seeds) < min_points: classifications[point_id] = NOISE return False else: classifications[point_id] = cluster_id for seed_id in seeds: classifications[seed_id] = cluster_id while len(seeds) > 0: current_point = seeds[0] results = _region_query(m, current_point, eps) if len(results) >= min_points: for i in range(0, len(results)): result_point = results[i] if classifications[result_point] == UNCLASSIFIED or \ classifications[result_point] == NOISE: if classifications[result_point] == UNCLASSIFIED: seeds.append(result_point) classifications[result_point] = cluster_id seeds = seeds[1:] return True def dbscan(m, eps, min_points): """Implementation of Density Based Spatial Clustering of Applications with Noise See https://en.wikipedia.org/wiki/DBSCAN scikit-learn probably has a better implementation Uses Euclidean Distance as the measure Inputs: m - A matrix whose columns are feature vectors eps - Maximum distance two points can be to be regionally related min_points - The minimum number of points to make a cluster Outputs: An array with either a cluster id number or dbscan.NOISE (None) for each column vector in m. """ cluster_id = 1 n_points = m.shape[1] classifications = [UNCLASSIFIED] * n_points for point_id in range(0, n_points): point = m[:,point_id] if classifications[point_id] == UNCLASSIFIED: if _expand_cluster(m, classifications, point_id, cluster_id, eps, min_points): cluster_id = cluster_id + 1 return classifications def test_dbscan(): m = np.matrix('1 1.2 0.8 3.7 3.9 3.6 10; 1.1 0.8 1 4 3.9 4.1 10') eps = 0.5 min_points = 2 assert dbscan(m, eps, min_points) == [1, 1, 1, 2, 2, 2, None] Bibliografia Tipos de Aprendizado https://lamfo-unb.github.io/2017/07/27/tres-tipos-am/ SVM https://www.inf.ufpr.br/dagoncalves/IA07.pdf https://towardsdatascience.com/support-vector-machine-introduction-to-machine-learning-al gorithms-934a444fca47 https://www.digitalhouse.com/br/blog/maquina-de-vetores-de-suporte/ https://shuzhanfan.github.io/2018/05/understanding-mathematics-behind-support-vector-mac hines/ https://github.com/mahesh147/Support-Vector-Machine/blob/master/support_vector_machin e.py Naive Bayes https://www.analyticsvidhya.com/blog/2017/09/naive-bayes-explained/ https://www.digitalhouse.com/br/blog/naive-bayes/ https://www.organicadigital.com/blog/algoritmo-de-classificacao-naive-bayes/ Random Forest https://www.ibm.com/cloud/learn/random-forest#toc-benefits-a-VrBNAC3d https://towardsdatascience.com/understanding-random-forest-58381e0602d2 Redes Neurais https://www.ibm.com/br-pt/cloud/learn/neural-networks https://aws.amazon.com/pt/what-is/neural-network/ https://www.deeplearningbook.com.br/o-que-sao-redes-neurais-artificiais-profundas/ https://sites.icmc.usp.br/andre/research/neural/ https://www.linkedin.com/pulse/artificial-neural-networks-advantages-disadvantages-maad-m -mijwel https://towardsdatascience.com/how-to-define-a-neural-network-as-a-mathematical-functionf7b820cde3f https://www.kdnuggets.com/2020/02/deep-neural-networks.html https://www.tutorialspoint.com/python_deep_learning/python_deep_learning_deep_neural_n etworks.htm https://towardsdatascience.com/a-laymans-guide-to-deep-neural-networks-ddcea24847fb https://github.com/TatevKaren/artificial-neural-network-business_case_study https://github.com/semicolon123/ML-DL-projects/blob/main/Convolutional_Neural_Network_f or_Classification_of_handwritten_digits.ipynb K-Means Clustering https://aprenderdatascience.com/k-means-clustering-agrupamento-k-means/ https://developers.google.com/machine-learning/clustering/algorithm/advantages-disadvanta ges https://www.linkedin.com/pulse/k-means-clustering-its-use-case-security-domain-anudeep-n alla/ Agglomerative Clustering https://www.datanovia.com/en/lessons/agglomerative-hierarchical-clustering/ https://www.educba.com/hierarchical-clustering-agglomerative/?source=leftnav DBSCAN https://towardsdatascience.com/how-dbscan-works-and-why-should-i-use-it-443b4a191c80 https://www.maxwell.vrac.puc-rio.br/24787/24787_6.PDF https://medium.com/@tarammullin/dbscan-parameter-estimation-ff8330e3a3bd https://towardsdatascience.com/dbscan-clustering-explained-97556a2ad556 NLP https://medium.com/neuronio-br/da-an%C3%A1lise-de-sentimentos-para-o-reconhecimentode-emo%C3%A7%C3%B5es-uma-hist%C3%B3ria-pln-171f27734c56 https://www.oracle.com/br/artificial-intelligence/what-is-natural-language-processing/ https://en.wikipedia.org/wiki/Natural_language_processing https://monkeylearn.com/natural-language-processing/ https://www.ibm.com/cloud/learn/natural-language-processing https://github.com/rmohashi/emotion-from-tweet