<jupyter_start><jupyter_text>Data Science Academy - Python Fundamentos - Capítulo 10 Download: http://github.com/dsacademybr<jupyter_code># Versão da Linguagem Python
from platform import python_version
print('Versão da Linguagem Python Usada Neste Jupyter Notebook:', python_version())<jupyter_output>Versão da Linguagem Python Usada Neste Jupyter Notebook: 3.8.8<jupyter_text>Introdução ao TensorFlow O Tensorflow é uma das bibliotecas mais amplamente utilizadas para implementar o aprendizado de máquina e outros algoritmos que envolvem grandes operações matemáticas. O Tensorflow foi desenvolvido pelo Google e é uma das bibliotecas de aprendizado de máquina mais populares no GitHub. O Google usa o Tensorflow para aprendizado de máquina em quase todos os aplicativos. Se você já usou o Google Photos ou o Google Voice Search, então já utlizou uma aplicação criada com a ajuda do TensorFlow. Vamos compreender os detalhes por trás do TensorFlow.Matematicamente, um tensor é um vetor N-dimensional, significando que um tensor pode ser usado para representar conjuntos de dados N-dimensionais. Aqui está um exemplo:<jupyter_code>from IPython.display import Image
Image('imagens/tensor1.png')<jupyter_output><empty_output><jupyter_text>A figura acima mostra alguns tensores simplificados com dimensões mínimas. À medida que a dimensão continua crescendo, os dados se tornam mais e mais complexos. Por exemplo, se pegarmos um Tensor da forma (3x3), posso chamá-lo de matriz de 3 linhas e colunas. Se eu selecionar outro Tensor de forma (1000x3x3), posso chamá-lo como tensor ou conjunto de 1000 matrizes 3x3. Aqui chamamos (1000x3x3) como a forma ou dimensão do tensor resultante. Os tensores podem ser constantes ou variáveis.<jupyter_code>from IPython.display import Image
Image('imagens/tensor2.png')<jupyter_output><empty_output><jupyter_text>TensorFlow x NumPy TensorFlow e NumPy são bastante semelhantes (ambos são bibliotecas de matriz N-d). NumPy é o pacote fundamental para computação científica com Python. Ele contém um poderoso objeto array N-dimensional, funções sofisticadas (broadcasting) e etc. Acredito que os usuários Python não podem viver sem o NumPy. O NumPy tem suporte a matriz N-d, mas não oferece métodos para criar funções de tensor e automaticamente computar derivadas, além de não ter suporte a GPU, e esta é uma das principais razões para a existência do TensorFlow. Abaixo uma comparação entre NumPy e TensorFlow, e você vai perceber que muitas palavras-chave são semelhantes.<jupyter_code>from IPython.display import Image
Image('imagens/tf_numpy.png')<jupyter_output><empty_output><jupyter_text>Grafo ComputacionalConheça a Formação Inteligência Artificial, um programa completo, 100% online e 100% em português, com mais de 402 horas em 9 cursos de nível intermediário/avançado, que vão ajudá-lo a se tornar um dos profissionais mais cobiçados do mercado de tecnologia. Clique no link abaixo, faça sua inscrição, comece hoje mesmo e aumente sua empregabilidade:https://www.datascienceacademy.com.br/bundle/formacao-inteligencia-artificialAgora sabemos o que o tensor realmente significa e é hora de entender o fluxo. Este fluxo refere-se a um grafo computacional ou simplesmente um grafo. Grafos computacionais são uma boa maneira de pensar em expressões matemáticas. O conceito de grafo foi introduzido por <NAME> em 1736 para tentar resolver o problema das Pontes de Konigsberg. Grafos são modelos matemáticos para resolver problemas práticos do dia a dia, com várias aplicações no mundo real tais como: circuitos elétricos, redes de distribuição, relações de parentesco entre pessoas, análise de redes sociais, logística, redes de estradas, redes de computadores e muito mais. Grafos são muito usados para modelar problemas em computação.Um Grafo é um modelo matemático que representa relações entre objetos. Um grafo G = (V, E) consiste de um conjunto de vértices V (também chamados de nós), ligados por um conjunto de bordas ou arestas E. Considere o diagrama abaixo:<jupyter_code>from IPython.display import Image
Image('imagens/grafo1.png')<jupyter_output><empty_output><jupyter_text>Existem três operações: duas adições e uma multiplicação. Ou seja: - c = a+b- d = b+1- e = c∗dPara criar um grafo computacional, fazemos cada uma dessas operações nos nós, juntamente com as variáveis de entrada. Quando o valor de um nó é a entrada para outro nó, uma seta vai de um para outro e temos nesse caso um grafo direcionado.Esses tipos de grafos surgem o tempo todo em Ciência da Computação, especialmente ao falar sobre programas funcionais. Eles estão intimamente relacionados com as noções de grafos de dependência e grafos de chamadas. Eles também são a principal abstração por trás do popular framework de Deep Learning, o TensorFlow.Para mais detalhes sobre grafos, leia um dos capítulos do Deep Learning Book:https://deeplearningbook.com.br/algoritmo-backpropagation-parte1-grafos-computacionais-e-chain-rule/ Um grafo para execução de um modelo de Machine Learning pode ser bem grande e podemos executar sub-grafos (porções dos grafos) em dispositivos diferentes, como uma GPU. Exemplo:<jupyter_code>from IPython.display import Image
Image('imagens/grafo2.png')<jupyter_output><empty_output><jupyter_text>A figura acima explica a execução paralela de sub-grafos. Aqui estão 2 operações de multiplicação de matrizes, já que ambas estão no mesmo nível. Os nós são executados em gpu_0 e gpu_1 em paralelo.  Modelo de Programação TensorFlow O principal objetivo de um programa TensorFlow é expressar uma computação numérica como um grafo direcionado. A figura abaixo é um exemplo de grafo de computação, que representa o cálculo de h = ReLU (Wx + b). Este é um componente muito clássico em muitas redes neurais, que conduz a transformação linear dos dados de entrada e, em seguida, alimenta uma linearidade (função de ativação linear retificada, neste caso).<jupyter_code>from IPython.display import Image
Image('imagens/grafo3.png')<jupyter_output><empty_output><jupyter_text>O grafo acima representa um cálculo de fluxo de dados; cada nó está em operação com zero ou mais entradas e zero ou mais saídas. As arestas do grafo são tensores que fluem entre os nós. Os Cientistas de Dados geralmente constroem um grafo computacional usando uma das linguagens frontend suportadas como Python e C ++ e, em seguida, iniciam o grafo. Vamos ver o grafo computacional acima em detalhes. Truncamos o grafo e deixamos a parte acima do nó ReLU, que é exatamente o cálculo h = ReLU (Wx + b).<jupyter_code>from IPython.display import Image
Image('imagens/grafo4.png')<jupyter_output><empty_output><jupyter_text>Podemos ver o grafo como um sistema, que tem entradas (os dados x), saída (h neste caso), variáveis com estado (W e b) e um monte de operações (matmul, add e ReLU). Deixe-me apresentar-lhe um por um.- Variáveis: quando treinamos um modelo, usamos variáveis para manter e atualizar parâmetros. Ao contrário de muitos tensores que fluem ao longo das margens do grafo, uma variável é um tipo especial de operação. Na maioria dos modelos de aprendizado de máquina, existem muitos parâmetros que temos que aprender, que são atualizados durante o treinamento. Variáveis são nós com estado que armazenam parâmetros e produzem seus valores atuais de tempos em tempos. Seus estados são mantidos em múltiplas execuções de um grafo. Por exemplo, os valores desses nós não serão atualizados até que uma etapa completa de treinamento usando um mini lote de dados seja concluída.- Operações matemáticas: Neste grafo, existem três tipos de operações matemáticas. A operação MatMul multiplica dois valores de matriz; A operação Add adiciona elementos e a operação ReLU é ativada com a função linear retificada de elementos.  Hello World<jupyter_code># Versão do TensorFlow a ser usada 
!pip install -q tensorflow==2.5
import tensorflow as tf
tf.__version__
# Cria um tensor
# Esse tensor é adicionado como um nó ao grafo.
hello = tf.constant('Hello, TensorFlow!')
print(hello)<jupyter_output>tf.Tensor(b'Hello, TensorFlow!', shape=(), dtype=string)<jupyter_text>Operações Matemáticas com Tensores  Soma<jupyter_code># Constantes
const_a = tf.constant(5)
const_b = tf.constant(9)
print(const_a)
# Soma
total = const_a + const_b
print(total)
# Criando os nodes no grafo computacional
node1 = tf.constant(5, dtype = tf.int32)
node2 = tf.constant(9, dtype = tf.int32)
node3 = tf.add(node1, node2)
 
# Executa o grafo
print("\nA soma do node1 com o node2 é:", node3)<jupyter_output>A soma do node1 com o node2 é: tf.Tensor(14, shape=(), dtype=int32)<jupyter_text>Subtração<jupyter_code># Tensores randômicos
rand_a = tf.random.normal([3], 2.0)
rand_b = tf.random.uniform([3], 1.0, 4.0)
print(rand_a)
print(rand_b)
# Subtração
diff = tf.subtract(rand_a, rand_b)
type(diff)
print('\nSubtração entre os 2 tensores é: ', diff)<jupyter_output>Subtração entre os 2 tensores é:  tf.Tensor([-0.39391327 -0.6512637  -1.852446  ], shape=(3,), dtype=float32)<jupyter_text>Divisão<jupyter_code># Tensores
node1 = tf.constant(21, dtype = tf.int32)
node2 = tf.constant(7, dtype = tf.int32)
# Divisão
div = tf.math.truediv(node1, node2)
print('\nDivisão Entre os Tensores: \n', div)<jupyter_output>Divisão Entre os Tensores: 
 tf.Tensor(3.0, shape=(), dtype=float64)<jupyter_text>Multiplicação Para aprender a teoria e prática sobre operações com matrizes acesse aqui.<jupyter_code># Criando tensores
tensor_a = tf.constant([[4., 2.]])
tensor_b = tf.constant([[3.],[7.]])
print(tensor_a)
print(tensor_b)
# Multiplicação
# tf.math.multiply(X, Y) executa multiplicação element-wise 
# https://www.tensorflow.org/api_docs/python/tf/math/multiply
prod = tf.math.multiply(tensor_a, tensor_b)
print('\nProduto Element-wise Entre os Tensores: \n', prod)
# Outro exemplo de Multiplicação de Matrizes
mat_a = tf.constant([[2, 3], [9, 2], [4, 5]])
mat_b = tf.constant([[6, 4, 5], [3, 7, 2]])
print(mat_a)
print(mat_b)
# Multiplicação
# tf.linalg.matmul(X, Y) executa multiplicação entre matrizes 
# https://www.tensorflow.org/api_docs/python/tf/linalg/matmul
mat_prod = tf.linalg.matmul(mat_a, mat_b)
print('\nProduto Element-wise Entre os Tensores (Matrizes): \n', mat_prod)
from IPython.display import Image
Image('imagens/matrizes.png')<jupyter_output><empty_output><jupyter_text>Usando Variáveis O TensorFlow também possui nodes variáveis que podem conter dados variáveis. Elas são usados principalmente para manter e atualizar parâmetros de um modelo de treinamento.Variáveis são buffers na memória contendo tensores. Elas devem ser inicializados e podem ser salvas durante e após o treinamento. Você pode restaurar os valores salvos posteriormente para exercitar ou analisar o modelo.Uma diferença importante a notar entre uma constante e uma variável é:O valor de uma constante é armazenado no grafo e seu valor é replicado onde o grafo é carregado. Uma variável é armazenada separadamente e pode estar em um servidor de parâmetros.<jupyter_code># Criado o mesmo tensor com tf.Variable() e tf.constant()
changeable_tensor = tf.Variable([10, 7])
unchangeable_tensor = tf.constant([10, 7])
changeable_tensor, unchangeable_tensor
# Isso vai gerar erro - requer o método assign()
changeable_tensor[0] = 7
changeable_tensor
# Isso vai funcionar
changeable_tensor[0].assign(7)
changeable_tensor
# Isso vai gerar erro (não podemos alterar tensores criados com tf.constant())
unchangeable_tensor[0].assign(7)
unchangleable_tensor<jupyter_output><empty_output><jupyter_text>Qual você deve usar? `tf.constant ()` ou `tf.Variable ()`?Dependerá do que o seu problema requer. No entanto, na maioria das vezes, o TensorFlow escolhe automaticamente para você (ao carregar dados ou modelar dados).  Outras Formas de Criar Tensores<jupyter_code># Tensor preenchido com 1
tf.ones(shape=(3, 2))
# Tensor preenchido com 0
tf.zeros(shape=(3, 2))
# Cria um tensor rank 4 (4 dimensões)
rank_4_tensor = tf.zeros([2, 3, 4, 5])
rank_4_tensor
# Imprime atributos do tensor
print("Tipo de dado de cada elemento:", rank_4_tensor.dtype)
print("Número de dimensões (rank):", rank_4_tensor.ndim)
print("Shape do tensor:", rank_4_tensor.shape)
print("Elementos no eixo 0 do tensor:", rank_4_tensor.shape[0])
print("Elementos no último eixo do tensor:", rank_4_tensor.shape[-1])
print("Número total de elementos (2*3*4*5):", tf.size(rank_4_tensor).numpy())
# Obtém os 2 primeiros itens de cada dimensão
rank_4_tensor[:2, :2, :2, :2]
# Obtém a dimensão de cada índice, exceto o final
rank_4_tensor[:1, :1, :1, :]
# Cria um tensor e rank 2 (2 dimensões)
rank_2_tensor = tf.constant([[10, 7],
                             [3, 4]])
# Obtém o último item de cada linha
rank_2_tensor[:, -1]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07297254654465131, 'fraction_numerical': 0.01593562638056169, 'mean_word_length': 5.497693490517683, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 57, 'https://': 4, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '60196', 'license': 'MIT', 'n_tokens_mistral': 4447, 'n_tokens_neox': 4302, 'n_words': 1594, 'path': 'Data Science Academy/PythonFundamentos/Cap10/Notebooks/DSA-Python-Cap10-Intro-TensorFlow.ipynb', 'repo_name': 'ivanbergon/Github_privado', 'size': '12684'}