Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
UNIVERSIDADE FEDERAL FLUMINENSE
MARCELO SOUZA DE MELO
DUAL SCALING: UMA IMPLEMENTAÇÃO EM GPU COM O
TENSORFLOW
Niterói
2018
MARCELO SOUZA DE MELO
DUAL SCALING: UMA IMPLEMENTAÇÃO EM GPU COM O
TENSORFLOW
Trabalho de Conclusão de Curso
submetido ao Curso de Tecnologia em
Sistemas de Computação da
Universidade Federal Fluminense como
requisito parcial para obtenção do título
de Tecnólogo em Sistemas de
Computação.
Orientador(a):
ALTOBELLI DE BRITO MANTUAN
NITERÓI
2018
MARCELO SOUZA DE MELO
DUAL SCALING: UMA IMPLEMENTAÇÃO EM GPU COM O
TENSORFLOW
Trabalho de Conclusão de Curso
submetido ao Curso de Tecnologia em
Sistemas de Computação da
Universidade Federal Fluminense como
requisito parcial para obtenção do título
de Tecnólogo em Sistemas de
Computação.
Niterói, 12 de junho de 2018.
Banca Examinadora:
_________________________________________
Prof. Altobelli de Brito Mantuan, Msc. – Orientador UFF – Universidade Federal Fluminense
_________________________________________ Prof. José Angel Riveaux Merino, Msc. – Avaliador
UFF – Universidade Federal Fluminense
Dedico este trabalho a minha mãe e aos
meus estimados filhos.
AGRADECIMENTOS
A Deus, que sempre me abençoou para alcançar mais uma conquista. A meu Orientador Altobelli de Brito Mantuan pela atenção e dedicação durante o trabalho de conclusão do curso. Aos Colegas de curso pelo incentivo e troca de experiências. A todos os meus familiares e amigos pelo apoio e colaboração.
RESUMO
Neste trabalho propomos uma abordagem ao leitor explicando como as técnicas de Dual Scaling simplificam a análise de dados, definem um maior relacionamento entre as variáveis e fornecem uma maior quantidade de dados que estão interligados em uma mesma categoria. No desenvolvimento do texto essas técnicas são representadas através de equações na implementação de algoritmos de complexidade O(m3) e são utilizadas funções da biblioteca TensorFlow, que trabalha com computação numérica de alto desempenho, para tratar os dados de entrada que são matrizes. As GPUs com sua arquitetura dedicada e paralelizada junto às técnicas de Dual Scaling têm o propósito de mostrar como é significativo o ganho de tempo no processamento, principalmente quando se trabalha com grande volume de dados. Testes de comparação feitos em placa gráfica em relação à CPU, usando os mesmos algoritmos, revelam a eficiência das placas mostrando a diferença percentual de tempo no resultado final. Palavras-chaves: Dual Scaling, TensorFlow e GPUs.
LISTA DE ILUSTRAÇÕES
Figura 1: Dados de Múltipla Escolha - Parte 1 ........................................................................ 31
Figura 2: Dados de Múltipla Escolha - Parte 2 ........................................................................ 33
Figura 3: Tabela de Contingência - Parte 1 ............................................................................. 35
Figura 4: Tabela de Contingência - Parte 2 ............................................................................. 36
Figura 5: Tabela de Contingência - Parte 3 ............................................................................. 38
Figura 6: Comparação do tempo de processamento para bases de incidência. ....................... 41
Figura 7: Comparação do tempo de processamento para bases de dominância. ..................... 42
LISTA DE TABELAS
Tabela 1: Tabela de Resposta Padrão ........................................................................................ 13
Tabela 2: Matriz de Padrão de Resposta ................................................................................... 13
Tabela 3: Tabela de Contingência ............................................................................................. 16
LISTA DE ABREVIATURAS E SIGLAS
TF -TensorFlow.
CPU - Central Processing Unit (Unidade de Processamento Central).
GPU - Graphics Processing Unit (Unidade de Processamento Gráfico).
API - Application Programming Interface (Interface de Programação de Aplicativo).
CUDA - Compute Unified Device Architecture (Computação de Arquitetura Unificada
de Dispositivo).
NAN - Not a Number (Não é um número).
SUMÁRIO
RESUMO..................................................................................................................... 6
LISTA DE ILUSTRAÇÕES ........................................................................................... 7
LISTA DE TABELAS .................................................................................................... 8
LISTA DE ABREVIATURAS E SIGLAS ....................................................................... 9
1 INTRODUÇÃO ...................................................................................................... 11
2 DUAL SCALING .................................................................................................... 12
2.1 DADOS CATEGÓRICO .................................................................................. 12
2.1.1 DADOS DE INCIDÊNCIA ........................................................................ 13
2.1.2 DADOS DE DOMINÂNCIA ..................................................................... 16
3 ALGORITMO DUAL SCALING EM GPU ............................................................... 20
3.1 COMPLEXIDADE DE UM ALGORITMO ........................................................ 20
3.2 TENSORFLOW .............................................................................................. 21
3.3 FUNCÕES TENSORFLOW ........................................................................... 22
3.4 DADOS DE INCIDÊNCIA ............................................................................... 31
3.5 DADOS DE DOMINÂNCIA ............................................................................. 35
4 TESTE DE TEMPO DE PROCESSAMENTO ........................................................ 40
5 CONCLUSÃO ........................................................................................................ 43
5.1 TRABALHOS FUTUROS ............................................................................... 43
REFERÊNCIA BIBLIOGRÁFICA ............................................................................... 44
11
1 INTRODUÇÃO
Com a grande quantidade de informações e a velocidade que elas chegam
atualmente, conseguir a separação e correlacionar todo esse conteúdo de forma que
tenhamos mais eficiência, não é uma tarefa simples. O aumento das informações traz
uma maior dificuldade e, nesse contexto, uma análise de dados se faz necessária.
Diante de todo esse cenário de crescimento, criam-se novas técnicas, funções,
algoritmos, hardware para melhorar o tempo de processamento dos dados sem, no
entanto, comprometer o resultado final.
Na proposta deste trabalho, podemos ver como foram desenvolvidos os
modelos matemáticos, Dual Scaling [1], utilizados para o mapeamento de indivíduos
e suas preferências a estímulos. Ressaltamos à importância do uso das GPUs junto
à implementação dessa técnica.
Além disso, é mostrada a biblioteca TensorFlow com suas funções para
resolver problemas que envolvem matrizes. E por fim, fazemos uma comparação dos
tempos dos algoritmos sequenciais confrontando com a implementação em GPU.
Este trabalho tem o intuito de mostrar o desenvolvimento do algoritmo Dual
Scaling em GPU usando a biblioteca TensorFlow [2]. Esse importante framework tem
se tornado uma das bibliotecas de Machine Learning disponíveis, com diversas
melhorias, otimizações, novos recursos e uma crescente comunidade. Dentre as
contribuições, usando este framework (TensorFlow), deste trabalho temos:
• Implementação do Dual Scaling para dados de incidência em GPU;
• Implementação do Dual Scaling para dados de Dominância em GPU;
• Comparação de desempenho de um programa sequencial com um programa
em paralelo (GPU).
2 DUAL SCALING
O Dual Scaling [1] é método versátil para análise de dados, reuni um
conjunto de técnicas relacionadas para a análise de uma ampla variedade de tipos de
dados categóricos, incluindo tabelas de contingência, dados de múltipla escolha,
dados ordenados, ordem de classificação e comparação pareada.
12 A análise de dados possui diferentes facetas e abordagens, incorporando
diversas técnicas. Tem grande importância em áreas como: ciências, estudos sociais
e negócios, por conta da diversidade de modelos possíveis. Tem como objetivo
descobrir relacionamentos entre as variáveis e obter delas a maior quantidade
possível dos dados categóricos multivariados. O Dual Scaling, proposto por Shizuhiko
Nishisato [1], é um método adequado para realizar tais funcionalidades.
2.1 DADOS CATEGÓRICO
Dados categóricos são aqueles que identificam para cada caso uma
categoria. As categorias podem ser derivadas de variáveis qualitativas (nominais ou
ordinais) ou quantitativas.
Existem duas categorias definidas por Shizuhiko Nishisato, são elas: dados
de incidência e dados de dominância. A primeira engloba os dados de múltipla escolha,
as tabelas de contingências e os dados ordenados; a segunda, os dados por ordem
de classificação e os dados de comparação pareada. Usando as técnicas do método
Dual Scaling, faremos um estudo mais detalhado dos dados de incidência e dos dados
de dominância. Aquele inclui os dados de múltipla escolha e este, tabela de
contingência.
2.1.1 DADOS DE INCIDÊNCIA
O cálculo do Dual Scaling para dados de incidência [18] proposto por
Shizuhiko Nishisato, são perguntas de múltipla escolha. Elas são versáteis, intuitivas
e geram dados limpos e fáceis de serem analisados. Como elas fornecem uma lista
fixa de opções de resposta, proporcionam respostas estruturadas e facilitam para seu
entrevistado concluir o questionário.
13 Baseado nas preferências para estímulos dos indivíduos, é feito um
questionário de opinião de múltipla escolha e logo após respondidas todas as
perguntas, transfere-se os resultados para uma tabela chamada de Tabela de
Resposta Padrão da seguinte forma: para cada pergunta feita no questionário, a
alternativa escolhida recebe o valor 1 e para as demais o valor 0. Concluída essa
etapa, com a base de dados D pronta, cria-se uma Matriz de Padrão de Resposta Fn,m,
onde n (linhas da matriz) representa os indivíduos e m (colunas da matriz) os possíveis
estímulos ou respostas de múltipla escolha.
Na apresentação dessa subseção, temos duas tabelas como exemplo de
transformação dos estímulos dos indivíduos (respostas) em uma matriz.
Tabela 1: Tabela de Resposta Padrão
Perguntas com três alternativas
Indivíduo P1 P2 P3 P4
1 100 001 001 001
2 100 001 100 001
3 001 100 010 001
4 100 100 001 001
5 001 100 001 010
Tabela 2: Matriz de Padrão de Resposta
Colunas (m)
Linhas (n) 1 2 3 4 5 6 7 8 9 10 11 12
1 1 0 0 0 0 1 0 0 1 0 0 1
2 1 0 0 0 0 1 1 0 0 0 0 1
3 0 0 1 1 0 0 0 1 0 0 0 1
4 1 0 0 1 0 0 0 0 1 0 0 1
5 0 0 1 1 0 0 0 0 1 0 1 0
O próximo passo é encontrar os vetores de frequência de linhas e colunas
de F, onde fr e fc são, respectivamente, somatório das linhas e somatório das colunas.
14
𝑓𝑟𝑖 =∑
𝑚
𝑘
𝐹𝑖,𝑘 (1)
𝑓𝑐𝑗 =∑
𝑛
𝑘
𝐹𝑘,𝑗 (2)
Diagonalizando fr e fc, encontra-se a matriz diagonal de linhas Dr e a matriz
diagonal de colunas Dc, ambas são quadradas e formadas em sua diagonal principal
pelos valores dos seus respectivos vetores.
𝐷𝑟𝑖,𝑞 = {𝑓𝑟𝑖𝑖 = 𝑞0𝑖 ≠ 𝑞
(3)
𝐷𝑐𝑟,𝑗 = {𝑓𝑐𝑟𝑟 = 𝑗0𝑟 ≠ 𝑗
(4)
Busca-se agora, uma matriz que defina as relações das variáveis entre as
colunas da matriz F. Indicaremos por Mm,m, a matriz procurada e cuja equação é:
𝑀 = 𝐹𝑇𝐷𝑟−1𝐹𝐷𝑐
−1, (5)
onde F t é a transposta de F, Dr-1 inversa de Dr e Dc
-1 inversa de Dc.
Sendo λ = { λ1, λ2, …, λm }, o vetor de autovalores e Vm,m, a matriz de
autovetores da matriz M, ordena-se os autovalores de forma decrescente e por
conseguinte as colunas de Vm,m devem acompanhar seus respectivos autovalores.
Após essa etapa, remove-se o primeiro valor de λ bem como a primeira coluna de
Vm,m. Observa-se também que o número máximo de elementos de λ e de colunas de
Vm,m deve ser iguais ao número de dimensões do espaço-solução ns, dado pela
equação
𝑛𝑠 = 𝑚 − 𝑞 − 1, (6)
onde m é o número de colunas (itens) de nossa matriz de padrão de respostas F, e q
é o número de categorias dos itens de resposta (questões).
15 Assim obtém-se λf ={ λ2, λ3, λ4, …, λn-1 }, como o vetor final de autovalores,
e Vfm,n como a matriz final de autovetores.
Dando sequência, deve-se calcular as matrizes T, o vetor tc (somatório de
todos os valores das colunas da matriz T), ft (somatório de todos os valores da matriz
de padrão de respostas F) e o vetor Cc.
𝑇 = 𝐷𝑐(𝑉𝑓 ∘ 𝑉𝑓) (7)
O símbolo ᵒ não representa o produto de matrizes e sim o produto de
Hadamard [3]. Neste caso, o resultado é o quadrado de cada valor da matriz Vf.
𝑡𝑐𝑝 =∑
𝑚
𝑘
𝑇𝑘,𝑝 (8)
𝑓𝑡𝑛,𝑚 = ∑𝐹𝑛,𝑚 (9)
𝐶𝑐𝑝 = √𝑓𝑡
𝑡𝑐𝑝 (10)
Calculemos agora, o vetor ρ (raiz quadrada dos autovalores finais), a matriz
N (peso padrão) e P (peso projetado).
𝜌𝑖 = √𝜆𝑓𝑖 (11)
Obtemos a matriz N, dado pelo produto de cada valor do vetor Cc para cada
valor das respectivas coluna de Vf, ou seja, o primeiro elemento do vetor multiplica
cada elemento da primeira coluna da matriz; o segundo elemento do vetor multiplica
cada elemento da segunda coluna da matriz e assim por diante.
𝑁𝑖,𝑝 = 𝑉𝑓𝑖,𝑝𝐶𝑐𝑝 (12)
Finalizando obtemos a matriz P, dado pelo produto de cada valor do vetor
ρ para cada valor das respectivas coluna de N, ou seja, o primeiro elemento do vetor
16 multiplica cada elemento da primeira coluna da matriz; o segundo elemento do vetor
multiplica cada elemento da segunda coluna da matriz e assim por diante.
Matriz de pesos projetados representa os valores do mapeamento dos itens
em cada uma das dimensões do espaço-solução. Neste espaço n dimensional pode
ser aplicadas técnicas de clusterização para agrupar itens semelhantes.
𝑃𝑖,𝑝 = 𝑁𝑖,𝑝𝜌𝑝 (13)
2.1.2 DADOS DE DOMINÂNCIA
O cálculo do Dual Scaling para dados de dominância [17] proposto por
Shizuhiko Nishisato é distinto daqueles para dados de incidência que são
representados por 0 e 1. O objetivo é quantificar os elementos dos dados que são
mensurações ordinais.
Na apresentação dessa subseção, temos uma tabela contingência como
exemplo de dados de dominância. A tabela de contingência é a tabela que calcula
observações por múltiplas variáveis categóricas. As linhas e colunas das tabelas
correspondem a essas variáveis categóricas.
Tabela 3: Tabela de Contingência
Colunas (m)
Linhas (n) 1 2 3 4 5 6 7 8 9
1 10 09 05 06 03 12 44 16 78
2 01 00 86 07 64 55 75 83 12
3 48 73 02 06 07 04 98 32 41
Em nossa implementação criamos a matriz de entrada Eoriginal, de
dimensões n (linhas) e m (colunas). Os cálculos são iniciados multiplicando a matriz
Eoriginal por um escalar -2, obtendo uma matriz E1.
(𝐸1)𝑛,𝑚 = −2 ⋅ (𝐸𝑜𝑟𝑖𝑔𝑖𝑛𝑎𝑙) (14)
17
Em seguida, somamos a E1, uma matriz Emais, de mesma dimensão, onde
seus valores são determinados por m (número de colunas de E1) + 1, obtendo Eadd.
(𝐸𝑎𝑑𝑑)𝑛,𝑚 = 𝐸1 + 𝐸𝑚𝑎𝑖𝑠 (15)
O próximo passo é encontrar a matriz C, obtida através do produto da
matriz transposta (Eadd)t e Eadd, representado por C1 e dividido por um escalar Q.
(𝐶1)𝑚,𝑚 = (𝐸𝑎𝑑𝑑)𝑡𝐸𝑎𝑑𝑑 (16)
𝑄 = (𝑛) ⋅ (𝑁) ⋅ (𝑛 − 1)2 (17)
(𝐶)𝑚,𝑚 = 𝐶1 𝑄⁄ (18)
Devemos agora encontrar os autovalores e os autovetores de C. Sendo D,
o vetor de autovalores e V, a matriz de autovetores, ordena-se os autovalores de forma
decrescente e por conseguinte as colunas de V devem acompanhar seus respectivos
autovalores.
Dando sequência, devemos encontrar a raiz quadrada dos autovalores de
D, representada ρ; a matriz x, através do produto de um escalar -1 e a matriz de
autovetores V; e uma matriz Xsqr, que é obtida com o quadrado de cada valor da matriz
x.
𝜌𝑖 = √𝜆𝑓𝑖. (19)
(𝑥)𝑛,𝑚 = −1 ⋅ 𝑉 (20)
(𝑋𝑠𝑞𝑟)𝑛,𝑚 = (𝑥)2 (21)
18 O próximo passo é encontrar Dc, uma matriz diagonal, onde sua diagonal
principal é formada pelo vetor fc.
𝑓𝑐𝑚 = 𝑛 ⋅ (𝑚 − 1) (22)
𝐷𝑐𝑟,𝑗 = {𝑓𝑐𝑟𝑟 = 𝑗0𝑟 ≠ 𝑗
(23)
Agora devemos obter uma matriz T com produto de Dc e Xsqr.
𝑇𝑛,𝑚 = 𝐷𝑐(𝑋𝑠𝑞𝑟) (24)
Encontramos também o vetor Cc, para isso achamos a raiz quadrada da
divisão de ft e tc (somatório de todos os valores das colunas da matriz T).
𝑓𝑡 = 𝑛 ⋅ 𝑚 ⋅ (𝑚 − 1) (25)
𝑡𝑐𝑜 =∑
𝑚
𝑘
𝑇𝑘,𝑜 (26)
𝐶𝑐𝑝 = √𝑓𝑡
𝑡𝑐𝑝 (27)
Dando continuidade, obtemos a matriz Xnormed, dado pelo produto de cada
valor do vetor Cc para cada valor das respectivas coluna de x, ou seja, o primeiro
elemento do vetor multiplica cada elemento da primeira coluna da matriz; o segundo
elemento do vetor multiplica cada elemento da segunda coluna da matriz e assim por
diante.
(𝑋𝑛𝑜𝑟𝑚𝑒𝑑)𝑛,𝑚 = 𝐶𝑐(𝑥) (28)
Em seguida, devemos achar a matriz Ynormed da mesma forma que obtemos
Xnormed. Porém temos que obter primeiro o vetor t, inverso do vetor L, que por sua vez,
é dado através do produto de fr e ρ. Após isso, encontramos a matriz Ynorm, dado pelo
19 produto de Eoriginal e Xnormed. Encontrado t e Ynorm, achamos a matriz Ynormed
multiplicando cada valor de t para cada valor das respectivas colunas da matriz Ynorm.
𝑓𝑟 = 𝑚 ⋅ (𝑚 − 1) (29)
𝐿𝑚 = 𝑓𝑟 ⋅ 𝜌 (30)
𝑡𝑚 = 1 𝐿⁄ (31)
(𝑌𝑛𝑜𝑟𝑚)𝑛,𝑚 = 𝐸𝑜𝑟𝑖𝑔𝑖𝑛𝑎𝑙(𝑋𝑛𝑜𝑟𝑚𝑒𝑑) (32)
(𝑌𝑛𝑜𝑟𝑚𝑒𝑑)𝑛,𝑚 = 𝑡𝑚(𝑌𝑛𝑜𝑟𝑚) (33)
Finalizando os cálculos, encontramos as matrizes Xprojected e Yprojected. Elas
são obtidas através do produto vetor ρ com as respectivas matrizes, da seguinte forma:
o primeiro elemento do vetor, multiplica cada elemento da primeira coluna da matriz;
o segundo elemento do vetor, multiplica cada elemento da segunda coluna da matriz
e assim por diante.
(𝑌𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑒𝑑)𝑛,𝑚 = 𝜌(𝑌𝑛𝑜𝑟𝑚𝑒𝑑) (34)
(𝑋𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑒𝑑)𝑛,𝑚 = 𝜌(𝑋𝑛𝑜𝑟𝑚𝑒𝑑) (35)
Essas matrizes têm a mesma finalidade que é a representação do
mapeamento dos itens da base para o espaço de n dimensões onde os itens são
projetados. Neste espaço n dimensional pode ser aplicadas técnicas de clusterização
para agrupar itens semelhantes.
3 ALGORITMO DUAL SCALING EM GPU
A cada dia fica evidente a importância de placas gráficas no auxílio ao
processamento de grande quantidade de informações [4]. Muitas inovações surgiram
para resolver problemas complexos. Com a linguagem CUDA [5] podemos explorar a
capacidade de processamento das placas, não só na computação gráfica, mas
também na científica.
20 As unidades de processamento gráfico (GPUs) que foram originalmente
projetadas para renderização de gráficos surgiram como coprocessadores
massivamente paralelos para a unidades de processamento central (CPUs). Estações
de trabalho multi-GPU de tamanho pequeno com centenas de elementos de
processamento podem acelerar substancialmente as aplicações científicas de
simulação com uso intensivo de computação. Por apresentarem arquitetura dedicada
e altamente paralelizada, as GPUs trazem muito mais ganhos do que as CPUs [6] .
O intuito de utilizar as GPUs em cálculos mais complexos é buscar
minimizar o tempo gasto no processamento sem alterar os resultados. É mostrar que
a exploração de paralelismo utilizando essa tecnologia pode fornecer um desempenho
computacional significativo e que os resultados dessa implementação proposta é mais
eficiente e pode ser indispensável para que simulações sejam realizadas com
qualidade e que tenham um menor custo.
3.1 COMPLEXIDADE DE UM ALGORITMO
A complexidade de um algoritmo é um mecanismo para entender e avaliar
um algoritmo em relação aos critérios como robustez, compatibilidade, desempenho
(rapidez), consumo de recursos (memória), portabilidade e outros, bem como saber
aplicá-los em problemas práticos.
No estudo da complexidade de algoritmo, é feito o uso de uma notação
chamada Notação (O) ou Big-O, usada para análise de pior caso de um algoritmo,
também chamado limite superior. A ideia da notação é descrever o comportamento
geral, também chamado de assintótico, do algoritmo em termos do crescimento do
número de operações conforme cresce o número de elementos processados.
Em nossos algoritmos de estudo, cuja complexidade é dada por O(m3) [7],
onde m representa o número de colunas da matriz, o uso das GPUs junto à escolha
de um melhor algoritmo para o processamento dos dados traz um ganho muito
significativo, abrindo um caminho de alta velocidade para o aprendizado de máquina,
possibilitando processar imensos conjuntos de dados, em tempo cada vez menor,
gerando modelos cada vez mais eficientes.
21
3.2 TENSORFLOW
É uma biblioteca de software de código aberto para computação numérica
de alto desempenho. Sua arquitetura flexível permite a fácil implementação em várias
plataformas como CPUs, GPUs, e de desktops a cluters de servidores para
dispositivos móveis e periféricos. Outras vantagens do TF [2] é principalmente a
disponibilização de APIs para linguagens de programação como Java [8], C++[9],
Python [10].
O TF foi lançado sob a licença de código aberto Apache 2.0 [11] em
novembro de 2015. Originalmente desenvolvido por pesquisadores e engenheiros que
trabalhavam na equipe do Google Brain [12] e na organização de Pesquisa de
Inteligência de Artificial do Google para a realização de pesquisas em máquinas e em
redes neurais profundas.
O nome TensorFlow é derivado das operações que as redes neurais
realizam em arrays ou tensores de dados multidimensionais, é literalmente um fluxo
de tensores. Essa arquitetura flexível permite que você implante computação para
uma ou mais CPUs ou GPUs em uma área de trabalho, servidor ou dispositivo móvel
sem reescrever o código. Embora contenha uma ampla gama de funcionalidades, o
TF é projetado principalmente para modelos de redes neurais profundas. O
aprendizado profundo é um subcampo de aprendizado de máquina que é um conjunto
de algoritmos inspirados pela estrutura e função do cérebro.
Devemos notar que o TF não tem como objetivo principal fornecer soluções
prontas de aprendizagem de máquina. Além disso, embora tenha suporte extensivo
para a funcionalidade específica de Machine Learning, é igualmente adequado para
executar cálculos matemáticos complexos. Existem dois fatores que levaram ao
surgimento e expansão do TF, Big Data [14] e Deep Learning [15].
As aplicações do TF são inúmeras. Podem ser usadas em pesquisa,
desenvolvimento, iteração através de novas arquiteturas de aprendizagem de
máquina, implementação dos modelos diretamente em produção a partir da conclusão
do treinamento, implementação em arquiteturas complexas existentes, criação e
treinamento de modelos para sistemas móveis e embarcados.
22
3.3 FUNCÕES TENSORFLOW
O TensorFlow [2], como o nome indica, é uma estrutura para definir e
executar cálculos envolvendo tensores. Um tensor é uma generalização de vetores e
matrizes para dimensões potencialmente mais altas. Internamente, o TF representa
os tensores como matrizes n-dimensionais de tipos de dados de base.
Na apresentação desse capítulo, vamos nos limitar a mostrar as funções
do TF com apenas os argumentos utilizados na implementação dos programas
multiple_choice_data e contingency_table.
tf.convert_to_tensor
tf.convert_to_tensor(value,dtype=None)
Argumentos:
• value: Um objeto cujo tipo tem uma função de conversão.
• dtype: Tipo de elemento opcional para o tensor retornado.
Retorna: Uma saída com base no valor.
Observações: Converte o valor fornecido em um tensor. Essa função converte objetos
Python de vários tipos em objetos tensor. Ele aceita objetos tensor, matrizes numpy,
listas e escalares Python.
tf.matrix_transpose
matrix_transpose(a,name='matrix_transpose')
Argumentos:
• a: Um tensor com rank >= 2.
• name: Nome opcional para o tensor.
23 Retorna: Um tensor onde o número de linhas e o de colunas são invertidos.
Observações: Determinar a transposta de uma matriz é reescrevê-la de forma que
suas linhas e colunas troquem de posições ordenadamente, isto é, a primeira linha é
reescrita como a primeira coluna, a segunda linha é reescrita como a segunda coluna
e assim por diante, até que se termine de reescrever todas as linhas na forma de
coluna.
A classificação de um objeto tf.Tensor é seu número de dimensões. Sinônimos para
rank incluem ordem ou grau ou n-dimensão.
tf.reduce_sum
tf.reduce_sum(input_tensor,axis=None)
Argumentos:
• input_tensor: O tensor para ser reduzido. Deve ter tipo numérico.
• axis: As dimensões para reduzir. Se Nenhum (o padrão), reduz todas as
dimensões. Deve estar no intervalo [-rank (input_tensor), rank (input_tensor)].
Retorna: Um tensor reduzido do mesmo tipo numérico que o input_tensor.
Observações: Em nossa implementação de dados de múltipla escolha, temos três
variáveis fc, fr e ft que representam, respectivamente, a soma das colunas, das linhas
e de todos os valores da matriz F.
fc = tf.reduce_sum(F,0)
fr = tf.reduce_sum(F,1)
ft = tf.reduce_sum(F)
tf.diag
tf.diag(x,name=None)
Argumentos:
24 • x: Um tensor. Deve ser um dos seguintes tipos: bfloat16, half, float32, float64,
int32, int64, complex64, complex128. Rank k tensor onde k é no máximo 1.
• name: Nome opcional para o tensor.
Retorna: Um tensor com os valores de x em sua diagonal principal.
Observações: Uma matriz diagonal é uma matriz quadrada onde os elementos que
não pertencem à diagonal principal são obrigatoriamente iguais a zero.
tf.matrix_inverse
tf.matrix_inverse(input)
Argumentos:
• input: Um tensor. Deve ser um dos seguintes tipos: float64, float32, complex64,
complex128. A forma é [..., M, M].
Retorna: Um tensor com mesmo tipo numérico e forma do tensor de entrada.
Observações: Se uma matriz não é invertível, não há garantia do retorno da operação.
Pode-se detectar a condição e gerar uma exceção ou simplesmente retornar um
resultado não válido.
tf.matmul
tf.matmul(a,b,name=None)
Argumentos:
• a: Tensor do tipo float16, float32, float64, int32, complex64, complex128 e rank>
1.
• b: Tensor com o mesmo tipo e classificação de a.
• name: Nome opcional para o tensor.
Retorna: Um tensor, onde o número de linhas é igual ao da matriz a, e o número de
colunas, igual ao a da matriz b.
25 Observações: Deve-se observar que a propriedade comutativa de um produto
elementar não é válida para um produto matricial, exceto quando as duas matrizes
são iguais.
tf.self_adjoint_eig
tf.self_adjoint_eig(tensor,name=None)
Argumentos:
• tensor: Tensor de forma [..., N, N].
• name: Nome opcional da operação.
Retorna:
• D: Autovalores. A forma é [..., N]. Ordenado em ordem não decrescente.
• X: Autovetores. A forma é [..., N, N].
Observações: Para que autovalores e autovetores retornem separados, devemos usar
a função da seguinte forma:
D,X = tf.self_adjoint_eig(M)
tf.multiply
tf.multiply(x,y,name=None)
Argumentos:
• x: Um tensor. Deve ser um dos seguintes tipos: bfloat16, half, float32, float64,
uint8, int8, uint16, int16, int32, int64, complex64, complex128.
• y: Um tensor. Deve ter o mesmo tipo de x.
• name: Um nome para a operação (opcional).
Retorna: Um tensor com o mesmo tipo numérico de x.
26 Observações: Como exemplo, a mesma função foi usada em nossa implementação
tanto para encontrar o produto de Hadamard quanto para encontrar N, matriz de peso
padrão.
H = tf.multiply(V,V)
N = multiply(V,Cc)
O retorno dessa função não é um produto matricial. Em H, temos o produto de duas
matrizes iguais, nesse caso particular a mesma matriz, onde cada elemento da
primeira multiplica o elemento que se encontra na mesma posição da segunda matriz.
Em N, temos um produto de uma matriz V e um vetor Cc, com o mesmo número de
coluna, onde primeiro elemento do vetor multiplica cada elemento da primeira coluna
da matriz; o segundo elemento do vetor, multiplica cada elemento da segunda coluna
da matriz e assim por diante. Nessa função, a propriedade comutativa não altera o
resultado final do produto.
tf.div
tf.div(x,y,name=None)
Argumentos:
• x: Numerador do tipo numérico real.
• y: Denominador do tipo numérico real.
• name: Um nome para a operação (opcional).
Retorna: x / y retorna o quociente de x e y.
Observações: Se um dos números, x ou y é um float, então o resultado será um float.
Caso contrário, a saída será um tipo inteiro.
tf.sqrt
tf.sqrt(x,name=None)
Argumentos:
27 • x: Um tensor. Deve ser um dos seguintes tipos: half, float32, float64, complex64,
complex128.
• name: Um nome para a operação (opcional).
Retorna: Um tensor com a raiz quadrada dos elementos de x.
Observações: Se o elemento for um número negativo, o retorno é dado por NAN.
Observe o retorno da matriz Raiz no exemplo:
X = tf.constant([[25.0, -25.0],[-16.0, 16.0]])
Raiz = tf.sqrt(X)
Matriz Raiz = [[5. nan]
[nan 4.]]
tf.divide
tf.divide(x,y,name=None)
Argumentos:
• x: Um tensor para ser dimensionado.
• y: Um tensor escalar 0-D.
• name: Um nome para a operação (opcional).
Retorna: Um tensor do mesmo tipo de x.
Observações: Nesta função, temos um escalar y dividindo todos os valores x.
tf.scalar_mul
tf.scalar_mul(scalar,x)
Argumentos:
• scalar: Um tensor escalar 0-D. Deve ter uma forma conhecida.
• x: Um tensor para ser dimensionado.
Retorna: Um tensor do mesmo tipo de x.
28 Observações: Nesta função, temos um escalar multiplicando todos os valores x. Se o
escalar não é um escalar 0-D, teremos um erro no retorno da função.
tf.add
tf.add(x,y,name=None)
Argumentos:
• x: Um tensor. Deve ser um dos seguintes tipos: bfloat16, half, float32, float64,
uint8, int8, int16, int32, int64, complex64, complex128, string.
• y: Um tensor. Deve ter o mesmo tipo de x.
• name: Um nome para a operação (opcional).
Retorna: Um tensor com o mesmo tipo de x.
Observações: O número de linhas e colunas de x e y deve ser igual.
tf.reverse
tf.reverse(x,axis,name=None)
Argumentos:
• x: Um tensor. Deve ser um dos seguintes tipos: uint8, int8, uint16, int16, int32,
int64, bool, half, bfloat16, float32, float64, complex64, complex128, string. Até
8-d.
• axis: Deve ser um dos seguintes tipos: int32, int64. 1-D. Os índices das
dimensões para reverter deve estar no intervalo [-rank (tensor), rank (tensor)].
• name: Um nome para a operação (opcional).
Retorna: Um tensor com o mesmo tipo de x.
Observações: Em nossa implementação, usamos a função reverse para colocar os
autovalores em ordem decrescente, já que a função self_adjoint_eig retorna em ordem
crescente. Usamos também a mesma função para inversão da matriz de autovetores
para acompanhar os respectivos valores do vetor de autovalores. Como exemplo
temos:
29
tf.slice
tf.slice(input_,begin,size,name=None)
Argumentos:
• input_: Um tensor.
• begin: Deve ser do tipo: int32 ou int64.
• size: Deve ser do tipo: int32 ou int64.
• name: Um nome para a operação (opcional).
Retorna: Um subtensor (fatia) do tensor de entrada.
Observações: Essa operação extrai uma fatia de uma entrada do tensor, começando
no local especificado por begin e o tamanho da fatia, por size. Devemos observar que
a contagem do número de linhas e colunas em Python inicia-se por 0. Observe:
V = tf.slice(Vr,[0,1],[m,q])
No exemplo acima, o retorno é uma matriz onde o início (begin) será a partir da linha
0 e da coluna 1 e o tamanho (size) terá m linhas e q colunas.
tf.fill
tf.fill(dims,value,name=None)
Argumentos:
• dims : Um tensor. Deve ser um dos seguintes tipos: int32, int64. 1-D, representa
forma do tensor de saída.
• value: Um tensor 0-D (escalar). Valor para preencher o tensor a ser retornado.
• name: Um nome para a operação (opcional).
Retorna: Um tensor. com o mesmo tipo de value.
Observações: Cria um tensor preenchido com um valor escalar.
30 tf.shape
tf.shape(input,name=None)
Argumentos:
• input: Um tensor.
• name: Um nome para a operação (opcional).
Retorna: A forma do tensor.
Observações: Essa operação retorna um tensor inteiro 1-D representando a forma da
entrada.
3.4 DADOS DE INCIDÊNCIA
Dividiremos o código multiple_choice_data em duas partes. Mostraremos,
através das linhas de código em Python, o mapeamento das funções do TensorFlow
com a importação de sua biblioteca como tf e onde foram aplicadas as equações
usando as técnicas do Dual Scaling.
Primeira parte do código
31
De acordo com a figura 1, temos:
• Na linha 68 do código, através da função convert_to_tensor, obtemos a matriz
de entrada F;
• Na linha 71 do código, através da função transpose, obtemos a transposta de
F, matriz ft;
• Na linha 74 do código, através da função reduce_sum, obtemos fr (vetor de
frequência de linha, somatório de todas as linhas de F), representado pela
equação (1);
• Na linha 78 do código, através da função diag, obtemos Dr (matriz diagonal
formada em sua diagonal principal por fr), representada pela equação (3);
• Na linha 81 do código, através da função matrix_inverse, obtemos Dr_inverse,
matriz inversa de Dr;
Figura 1: Dados de Múltipla Escolha - Parte 1
32 • Na linha 84 do código, através da função matmul, obtemos a matriz A,
resultante do produto das matrizes ft e Dr_inverse;
• Na linha 87 do código, através da função matmul, obtemos a matriz B,
resultante do produto das matrizes A e F;
• Na linha 90 do código, através da função reduce_sum, obtemos fc (o vetor de
frequência de coluna, somatório de todas as colunas F), representado pela
equação (2);
• Na linha 94 do código, através da função diag, obtemos Dc (matriz diagonal
formada em sua diagonal principal por fc), representada pela equação (4);
• Na linha 97 do código, através da função matrix_inverse, obtemos Dc_inverse,
matriz inversa de Dc;
• Na linha 100 do código, através da função matmul, para obter M (matriz
resultante do produto das matrizes B e Dc_inverse), representada pela
equação (5);
Segunda parte do código
33
De acordo com a figura 2, temos:
• Na linha 103 do código, através da função self_adjoint_eig, obtemos o vetor D,
de autovalores, e a matriz X, de autovetores, ambos da matriz M;
• Na linha 106 do código, através da função reverse, obtemos er, vetor de
autovalores de D em ordem decrescente;
• Na linha 107 do código, através da função reverse, obtemos Vr, matriz de
autovetores de X ordenado com os autovalores;
• Na linha 110 do código, através da função slice, obtemos e, vetor de
autovalores de er com o número de dimensões do espaço solução reduzido;
Figura 2: Dados de Múltipla Escolha - Parte 2
34 • Na linha 111 do código, através da função slice, obtemos V, matriz de
autovetores de Vr com o número de dimensões do espaço solução reduzido;
• Na linha 114 do código, através da função multiply, obtemos H, matriz com
todos elementos de V elevados ao quadrado;
• Na linha 117 do código, através da função matmul, obtemos T (matriz resultante
do produto das matrizes Dc e H), representada pela equação (7);
• Na linha 120 do código, através da função reduce_sum, obtemos o vetor de
frequência de coluna tc (somatório de todas colunas de T), representado pela
equação (8);
• Na linha 123 do código, através da função reduce_sum, obtemos ft (soma de
todos os valores de F), representado pela equação (9);
• Na linha 126 do código, através da função div, obtemos G, obtemos um vetor
resultante da divisão de ft por tc;
• Na linha 129 do código, através da função sqrt, obtemos Cc (vetor resultante
da raiz quadrada da divisão de ft por tc), representado pela equação (10);
• Na linha 132 do código, através da função sqrt, obtemos Rho (vetor resultante
da raiz quadrada do vetor de autovalores e), representado pela equação (11);
• Na linha 134 do código, através da função multiply, obtemos N (matriz de peso
padrão resultante do produto de cada valor do vetor de Cc para cada valor da
respectiva coluna de V), representada pela equação (12);
• Na linha 137 do código, através da função multiply, obtemos P (matriz de peso
projetado resultante do produto de cada valor do vetor Cc para cada valor da
respectiva coluna de N), representada pela equação (13).
3.5 DADOS DE DOMINÂNCIA
35 Na apresentação dessa subseção, dividiremos o código contingency_table
em três partes. Mostraremos, através das linhas de código em Python, o mapeamento
das funções do TensorFlow com a importação de sua biblioteca como tf e onde foram
aplicadas as equações usando as técnicas do Dual Scaling.
Primeira parte do código
De acordo com a figura 3, temos:
• Na linha 69 do código, através da função convert_to_tensor, obtemos a matriz
de entrada Eoriiginal,;
• Na linha 72 do código, através da função scalar_mul, obtemos E1 (matriz
resultante do produto de um escalar -2 e a matriz Eoriiginal), sendo representada
pela equação (14);
• Na linha 75 do código, através da função fill, obtemos a matriz Emais (matriz
onde seus valores são formados por m + 1);
• Na linha 78 do código, através da função add, obtemos Eadd (matriz resultante
do somatório das matrizes E1 e Emais ), sendo representada pela equação (15);
• Na linha 81 do código, através da função matmul e da função transpose,
obtemos C1 (matriz resultante do produto da transposta de Eadd e a Eadd), sendo
representada pela equação (16);
Figura 3: Tabela de Contingência - Parte 1
36 • Na linha 84 do código, Q, número determinado por uma expressão que envolve
n e m, representado pela equação (17);
• Na linha 87 do código, através da função divide, obtemos C (matriz resultante
da divisão da matriz C1 e o escalar Q), sendo representada pela equação (18);
Segunda parte do código
De acordo com a figura 4, temos:
• Na linha 89 do código, através da função self_adjoint_eig, obtemos o vetor D,
de autovalores, a matriz V, de autovetores, ambos da matriz C;
• Na linha 92 do código, através da função reverse, obtemos Drev, vetor de
autovalores de D em ordem decrescente;
• Na linha 95 do código, através da função slice, obtemos D, vetor de autovalores
de Drev com o último valor removido;
Figura 4: Tabela de Contingência - Parte 2
37 • Na linha 98 do código, através da função reverse, obtemos Vrev, matriz de
autovetores de V ordenado com os autovalores;
• Na linha 101 do código, através da função slice, obtemos V, matriz de
autovetores de Vr com o número de dimensões do espaço solução reduzido;
• Na linha 104 do código, através da função sqrt, obtemos Rho (vetor resultante
da raiz quadrada do vetor de autovalores D), sendo representado pela equação
(19);
• Na linha 107 do código, através da função scalar_mul, obtemos x (matriz
resultante do produto de um escalar -1 e a matriz V), sendo representada pela
equação (20);
• Na linha 110 do código, através da função multiply, obtemos Xsqr (matriz com
todos elementos de x elevados ao quadrado), sendo representada pela
equação (21);
• Na linha 113 do código, através da função fill, obtemos a vetor fc, (matriz
formada por uma exprssão que envolve n e m), sendo representado pela
equação (22);
• Na linha 116 do código, através da função diag, obtemos a matriz diagonal Dc
(formada em sua diagonal principal por fc), sendo representada pela equação
(23);
• Na linha 119 do código, através da função matmul, obtemos T (matriz resultante
do produto das matrizes Dc e Xsqr), sendo representada pela equação (24);
• Na linha 122 do código, ft, número determinado por uma expressão que
envolve n e m, representado pela equação (25);
Terceira parte do código
38
De acordo com a figura 5, temos:
• Na linha 125 do código, através da função reduce_sum, obtemos o vetor de
frequência de coluna tc (somatório de todas colunas de T), sendo representado
pela equação (26);
• Na linha 128 do código, através da função div, obtemos G ( vetor resultante da
divisão ft por tc);
• Na linha 131 do código, através da função sqrt, obter Cc (vetor resultante da
raiz quadrada da divisão de ft por tc ) sendo representado pela equação (27);
• Na linha 134 do código, através da função multiply, obtemos Xnormed (matriz
resultante do produto de cada valor do vetor Cc para cada valor da respectiva
coluna da matriz x), sendo representada pela equação (28);
Figura 5: Tabela de Contingência - Parte 3
39 • Na linha 137 do código, fr, número determinado por uma expressão que
envolve m, representado pela equação (29);
• Na linha 140 do código, através da função scalar_mul, obtemos L (vetor
resultante do produto de um escalar fr e vetor Rho), sendo representado pela
equação (30);
• Na linha 143 do código, através da função div, obtemos t (vetor resultante do
inverso dos valores de L), sendo representado pela equação (31);
• Na linha 146 do códiigo, através da função matmul, obtemos Ynorm (matriz
resultante do produto das matrizes Eoriginal e Xnormed), sendo representada pela
equação (32);
• Na linha 149 do código, através da função multiply, obtemos Ynormed (matriz
resultante do produto de cada valor do vetor t para cada valor da respectiva
coluna de Ynorm), sendo representada pela equação (33)
• Na linha 152 do código, através da função multiply, obtemos Xprojected (matriz
resultante do produto de cada valor do vetor Rho para cada valor da respectiva
coluna de Xnormed), sendo representada pela equação (34);
• Na linha 155 do código, através da função multiply, obtemos Yprojected (matriz
resultante do produto de cada valor do vetor Rho para cada valor da respectiva
coluna de Ynormed), sendo representada pela equação (35);
• Na linha 157 do código, obtemos a através da função shape (retorna as
dimensões da matriz Xprojected);
• Na linha 158 do código, obtemos b através da função shape (retorna as
dimensões da matriz Yprojected);
4 TESTE DE TEMPO DE PROCESSAMENTO
O objetivo deste trabalho foi desenvolver os algoritmos de Dual Scaling de
forma paralela rodando em GPU. Para validar os nossos algoritmos, desenvolvido em
40 Python usando TensorFlow, vamos comparar com a implementação sequencial em
C++ sem uso de TensorFlow. Embora sejam linguagem diferentes, uma compilada
(C++) e a outra interpretada (Python), a diferença de tempo de execução dos dois
cenários é disprezível, pois o algoritmo C++ é sequencial enquanto o Python é paralelo.
Os experimentos foram realizados em um PC, com sistema operacional Windows 8
64 bits, com CPU Intel I7 4.0GHz, placa de vídeo NVIDIA Geforce GTX 760 com 2GB
de memória e 16GB de memória RAM.
Nossos testes foram realizados em bases reais fornecidas por LUCS-KDD
ARM e CARM biblioteca [15]. Infelizmente, são foram selecionadas 5 bases porque
as outras não couberam na memória de vídeo, impossibilitando assim a comparação
do algoritmo sequencial e paralelo. Para bases de incidência foi selecionado duas
bases: mFeat com 2000 transações e 1648 iten; e WaveForm com 5000 transações e
98 itens. Para bases de Dominância foi selecionado três bases: Chess com 3196
transações e 75 itens; BMS com 59602 transações e 497 itens; e connect-4 com
67557 transações e 129 itens.
O primeiro experimento foi realizado em base de incidência, veja Figura 6.
Nesse teste podemos perceber que o algoritmo em GPU teve um desempenho muito
superior para base mFeat, cujo o tempo em GPU foi 7,30 segundos contra 136.4
segundos em CPU. O mesmo comportamento não foi observado para a segunda base
Waveform, onde o tempo em CPU foi 3,30 segundos contra 9,45 segundos da GPU.
41
Esse comportamento acontece por dois motivos, o primeiro é o TensorFlow
não implementa matriz esparsa, consequentemente, as matrizes diagonais Dc e Dr
são altamente esparsas. A implementação em C++ utiliza essa propriedade para evitar
contas desnecessárias. Outro problema, que foi observado, foi o tempo de
comunicação para copiar a matriz da memória principal para GPU e vice-versa. Logo,
para matrizes com poucos itens (m), o tempo de comunicação com GPU gera um
overheard no tempo final de processamento.
O segundo experimento foi realizado com bases de dominância, veja Figura
7. A base Chess, teve um desempenho na GPU 1,60 segundos, contra 2,90 segundos
da CPU. A base BMS, o tempo de processamento foi 18,20 segundos em GPU, contra
Figura 6: Comparação do tempo de processamento para bases de incidência.
42 21,90 segundos em CPU. A última base Connec4, teve o tempo de processamento
em 2,75 segundos em GPU, contra 5,71 segundos em CPU. Neste teste percebemos
que a base connect teve maior diferença de tempo de processamento. Os mesmos
problemas que ocorreram nas bases de incidência, aconteceu nas bases de
dominância.
Durante os experimentos percebemos que uma das desvantagens do
TensorFlow é que a biblioteca não implementa matrizes esparsas. Isso limitou o uso
do algoritmo, uma vez que, para determinadas bases a matriz diagonal Dr tinha um
tamanho muito superior comportado pela placa de vídeo. Desta forma a
implementação ficou muito limitada ao tamanho de transações, para o caso de base
de incidência.
5 CONCLUSÃO
Em nosso trabalho buscamos apresentar um estudo que fornecesse uma
melhora no tratamento das informações, como obter resultados mais simplificados,
mais rápidos e que mantivesse confiabilidade na solução final. Para que esses
objetivos fossem alcançados, utilizamos método Dual Scaling, que reuni várias
Figura 7: Comparação do tempo de processamento para bases de dominância.
43 técnicas para trabalhar com bases de dados complexas, para analisar e gerar
informações mais simplificadas, devido ao seu método multidimensional e não linear
de quantificação; utilizamos GPUs, com suas arquiteturas paralelizadas, para
aumentar o desempenho no processamento dos dados e com isso diminuir o tempo
de resposta; fizemos uso da biblioteca TensorFlow para fornecer um conjunto extenso
de funções que permitem aos usuários definir modelos personalizados e flexíveis de
forma rápida e intuitiva. Porém, ao longo do projeto, embora tenha cumprida alguns
pontos, foram observadas limitações no que se refere a algumas bases de dados por
não trabalhar com matrizes esparsas em GPUs. Uma alternativa para esse problema
é utilização do Cupy , framework genérico para qualquer tipo de base.
5.1 TRABALHOS FUTUROS
Um possível trabalho futuro é explorar a matriz de pesos projetados para
calcular a matriz de distância quadrada entre eles, pois quanto menor a distância ente
os itens, maior o relacionamento entre eles. Obter um valor delta, percentual que
representa um todo no espaço-solução, para calcular cada uma das dimensões do
espaço-solução.
44
REFERÊNCIA BIBLIOGRÁFICA
1. NISHISATO, S. Elements of Dual Scaling: An Introduction To Practical Data Analysis.
[S.l.]: Psychology Press.,1993.
2. ABADI, Martín; AGARWAL, Ashish.TensorFlow: Large-scale Machine Learning on
Heterogeneous Systems. <htttp://www.tensorflow.org>Acesso em 20 mar. 2018.
3. NISHISATO, Shizuhiko. On quantifying different types of categorical data.
Psychometrika .,1993.
4. Hadamard product (matrices). In: Wikipédia: a enciclopédia livre.
<https://en.wikipedia.org/wiki/Hadamard_product_(matrices) > Acesso em 01 jun.
2018.
5. RIOS, E. Exploração de Estratégias de Busca Local em Ambientes CPU/GPU,2016.
Tese de Doutorado, Programa de Pós-graduação em Computação, Instituto de
Computação, Universidade Federal Fluminense.
6. CUDA. <https://developer.nvidia.com/cuda-zone>Acesso em 03 mar. 2018.
7. DETOMINI, Renan Correa . Using GPU to exploit parallelism on cryptography. In:
Information Systems and Technologies., 2011.
8. COUTINHO, Demétrios. Complexidade Algoritmo. <https://docente.ifrn.edu.br/>
Acesso em 15 de mai. 2018.
9. DEITEL, Paul; DEITEL, Harvey. Java How to Program, 2014. Curso de Graduação
em Sistema de Informação, MIT Sloan School of Management.
10. JAMSA, Kris; KLANDER, Lars. Jamsa's C/C++ Programmer's Bible: The Ultimate
Guide to C/C++ Programming, 1997. Doutorado em ciência da computação com ênfase
em sistemas operacionais , Arizona State University, Arizona.
11. VAN ROSSUM, Guido; DRAKE, Fred L. The Python Language Reference Manual.
Network Theory Ltd, 2011. Mestrado na Universidade de Amsterdã, Holanda.
12. McCOOL, Robert. Apache Software Foundation. <http://www.apache.org> Acesso
em 25 abr. 2018.
13. Google Brain. <https://ai.google/research/teams/brain>Acesso em 12 abr. 2018.
45 14. TAURION, Cezar. Big data, 2013. Mestrado em Ciência da Computação.
15. GOODFELLOW, Ian. Deep learning, 2016. MIT, Cambridge.
16. Coenen. The LUCS-KDD discretised/normalised ARM and CARM data library.
<http://www.csc.liv.ac.uk/∼frans/KDD/Software/LUCS-KDD-
DN/DataSets/dataSets.html> Acessado em 30 mai. 2018.
17. Nishisato, Shizuhiko. Optimal scaling of paired comparison and rank order
data: An alternative to guttman's formulation. 1978. Psychometrika.
18. Nishisato, Shizuhiko and Sheu, Wen-Jenn. A note on dual scaling of successive
categories data. 1984. Psychometrika.