Agora que você já sabe o que é
um modelo de IA generativa,
nós vamos dar um mergulho
mais profundo e começar a ver
como podemos implementar
esses modelos na prática.
Para isso, precisamos conhecer
a base desses modelos.
A base para modelos
de IA generativas
são redes neurais
artificiais,
e a base para redes neurais
artificiais é álgebra linear.
Então, nesse laboratório, veremos
uma revisão sobre a álgebra linear
utilizando a biblioteca
NumPy do Python.
Álgebra linear, basicamente, é
como podemos fazer operações
com vetores e matrizes.
Então, para começar
esse laboratório,
a gente vai começar a relembrar
um pouco sobre vetores
e ver como podemos representar
esses vetores no Python.
Para isso, vamos utilizar
a biblioteca NumPy do Python.
Essa biblioteca é especializada
em cálculo numérico e álgebra linear,
então é a principal biblioteca
do Python para esse fim.
Então, vamos começar o código
mportando essa biblioteca
para dentro do código.
Então, "import numpy as np".
Bom, para definir
um vetor no NumPy,
eu posso utilizar o módulo
"array" do NumPy.
Então, se eu criar
um "vetor_a = np.array"
e passar uma lista
de valores para ele,
ele vai criar
um vetor para mim.
Se eu quiser criar
um segundo vetor, "b",
eu também posso
utilizar o mesmo código.
Então, "np.array", vou passar
uma lista de valores
e será criado um array, ou um vetor,
e associado à variável "vetor_b".
Nesse código aqui, vamos
criar dois vetores, "A" e "B",
e vamos printar
esses vetores.
Então, aqui nas respostas,
vetor A e vetor B,
da forma como a gente
criou anteriormente,
e as dimensões do vetor A,
por exemplo, o vetor B tem
a mesma dimensão do vetor A.
Então, o que é a dimensão
de um vetor?
São quantos elementos
tem nesse vetor.
Então, esse vetor tem
três elementos, perfeito?
Se eu quiser fazer a soma
desses dois vetores,
eu posso simplesmente vir aqui
e criar uma variável "soma_vetores",
que vai ser igual
a "vetor_a + vetor_b".
E, aqui, eu vou printar
a soma desses vetores.
Então, quando nós somamos dois
vetores, qual a operação é feita?
Basicamente, uma soma,
elemento a elemento,
então cada elemento
do vetor A
vai ser somado com o mesmo
elemento do vetor B,
e eu vou ter esse
resultado aqui na soma.
Se eu quiser fazer uma subtração,
da mesma forma, "vetor_a - vetor_b",
e o resultado é uma subtração
elemento a elemento.
Então, eu posso, por exemplo, criar
uma variável "vetor_multiplicado",
que vai ser igual ao vetor_a vezes
um valor escalar, por exemplo, "2".
Essa multiplicação também
será feita elemento a elemento,
então cada elemento do vetor A
será multiplicado por 2,
e aqui a gente tem,
no nosso código,
a resposta desse
vetor multiplicado.
Então, 1 vezes 2,
2 vezes 2, 3 vezes 2,
e a resposta aqui na variável
"vetor_multiplicado".
Uma outra operação
muito importante,
que eu posso fazer
com dois vetores,
se chama "produto escalar".
O produto escalar
entre dois vetores
é uma operação onde eu vou
multiplicar esses dois vetores
e o resultado será
um número escalar.
O que esse número
representa?
Esse número representa
um certo grau de similaridade
entre esses dois
vetores, ou seja,
se esses vetores
estiverem apontando
para uma mesma direção,
esse produto será maior.
Se esses produtos estiverem
perpendiculares um com o outro,
ou seja, se tiver um ângulo
de 90 graus entre eles,
esse produto escalar vai
retornar algo próximo de 0.
E se eles estiverem apontando
para direções opostas,
isso significa que esse produto
escalar terá um valor negativo.
Então, basicamente,
o produto escalar
é uma forma de você
multiplicar dois vetores
e obter um escalar
como resposta,
e esse escalar tem
um certo significado,
indica, mais ou menos,
se esses dois vetores
estão apontando
para direções parecidas.
Como eu calculo isso?
Por trás dos panos,
eu estou fazendo
linhas de um vetor
versus colunas do outro.
Então, se for necessário,
por exemplo, nesse caso,
a gente tem vetores
de mesmas dimensões,
o segundo vetor vai ser
transposto, nesse caso.
Então, se eu fizer, aqui, o produto
escalar do vetor A com o vetor B,
eu vou ter um número, 32.
Esse número indica,
de certa forma,
se esses dois vetores
estão apontando
para direções similares
ou opostas.
Outro conceito
muito importante
quando a gente fala
de álgebra linear
é o conceito de matrizes.
Então, diferentemente dos vetores,
que têm apenas uma dimensão,
as matrizes podem ter
duas dimensões.
Então, as matrizes vão
ter linhas e colunas.
Então, serão parecidas
com uma tabelinha,
que teria linhas e colunas.
Então, eu posso criar, aqui
no Python, uma matriz X,
usando o np.array,
e, em vez de passar apenas
uma lista de valores,
eu vou passar uma lista
de listas de valores,
onde cada lista vai representar
uma linha dessa matriz.
Essa matriz tem duas linhas,
essa linha e essa linha,
e duas colunas, essa
coluna e essa coluna.
Então, é uma matriz que tem
dimensões 2 por 2.
A matriz Y também é
uma matriz 2 por 2,
porque ela tem duas
linhas e duas colunas.
Se a gente rodar esse código,
a gente vai ver essas duas matrizes
dispostas, aqui, da forma
que a gente criou elas, perfeito?
As operações que a gente
pode fazer com as matrizes
são muito similares às operações
que a gente pode fazer
com os vetores.
Então, eu posso olhar
para o shape dessa matriz,
vai me dar
as dimensões dela,
então quais são
as dimensões dessa matriz?
No vetor, a gente tinha
apenas uma dimensão,
então a gente criou
um vetor de três posições.
Aqui, eu tenho duas dimensões,
eu tenho linhas e colunas,
então cada uma das nossas matrizes
tem duas linhas e duas colunas.
Isso, a gente consegue saber usando
o atributo "shape" da nossa matriz.
Da mesma forma
como vetores,
como a gente fez
para os nossos vetores,
a gente pode somar matrizes.
Então, quando eu somo
a matriz X com a matriz Y,
eu estou fazendo a mesma operação
que a gente fez para os vetores,
então eu estou somando,
elemento a elemento,
os valores das duas matrizes.
Então, se a gente fizer
a soma e printar essa soma,
a gente vai ter o resultado,
que é a soma das duas matrizes,
elemento a elemento.
Da mesma forma,
se a gente fizer a subtração
entre essas duas matrizes,
eu também vou ter o resultado
como sendo uma matriz resultante
de mesma dimensão, 2 por 2,
onde cada valor é a subtração,
elemento a elemento,
dessas duas matrizes.
Por fim, eu posso multiplicar
essa matriz, ou alguma matriz,
por um valor escalar, e aí eu
também terei uma resposta
que vai ser a multiplicação desse
escalar, por exemplo, o valor "3",
por cada elemento
dessa matriz.
Então, o resultado também
é uma matriz 2 por 2,
e essa matriz 2 por 2,
cada elemento dela
é o resultado da multiplicação
desse escalar pelo valor da matriz.
Da mesma forma que fizemos
o produto escalar para vetores,
podemos, também,
aplicar para matrizes,
porém, com uma pequena
diferença.
Nos vetores, temos
apenas uma dimensão,
nas matrizes, a gente
tem duas dimensões.
Então, se eu criar
uma matriz, por exemplo,
chamada "matriz_a_mult",
como uma matriz que vai ter
duas linhas e três colunas,
eu utilizaria esse código
aqui para criá-la.
Poderia criar, também,
uma "matriz_b_mult",
essa matriz ao invés de ter
duas linhas por três colunas,
vai ter três linhas
por duas colunas.
Então, se a gente rodar
esse código aqui,
a gente pode ver, aqui,
as nossas duas matrizes.
Uma tem dimensões dois por três,
uma tem dimensões três por dois.
Para fazer o produto escalar
dessas duas matrizes,
eu posso utilizar o mesmo
código do NumPy,
a mesma função,
o mesmo método,
"np.dot", "matriz_a_mult"
e "matriz_b_mult",
e eu vou ter o resultado
dessa multiplicação.
Note que o resultado
dessa multiplicação
é uma matriz de 2 por 2.
Então, existe uma regrinha
para a gente fazer multiplicação
matricial através do produto escalar
e essa multiplicação
ser possível.
Qual seria essa regra?
As dimensões da minha matriz A,
a quantidade de colunas dela
tem que ser igual à quantidade
de linhas da matriz B.
Então, sempre que eu estiver
fazendo o produto escalar
entre duas matrizes, a quantidade
de colunas da primeira
tem que ser igual à quantidade
de linhas da segunda.
E o resultado sempre será
a quantidade de linhas da primeira
por a quantidade
de colunas da segunda.
Então, essas duas matrizes,
eu posso multiplicar, por quê?
Porque elas obedecem
o primeiro quesito,
a quantidade de colunas
da matriz A
é igual à quantidade
de linhas da matriz B.
E o resultado dessa conta
desse produto escalar
vai dar a quantidade
de linhas da matriz A
pela quantidade de colunas
da matriz B, que vai dar 2 por 2.
Então, se a gente pudesse
fixar um pouco essa regra,
você poderia pensar assim: quando
eu for multiplicar duas matrizes,
fazer o produto escalar delas,
os valores de dentro, ou seja,
a quantidade de colunas da primeira
e de linhas da segunda
tem que ser igual, e o resultado
vai dar os dois de fora, ou seja,
a quantidade de linhas
da primeira
e a quantidade
de colunas da segunda.
Então, seguindo aqui,
vamos passar, agora,
por outras operações relevantes
no nosso contexto de álgebra linear.
Um conceito mega importante,
uma operação mega importante
é inverter uma matriz.
Para inverter uma matriz, qualquer
matriz tem que ser quadrada,
como a que a gente
está criando aqui,
"matriz_quadrada = np.array",
uma matriz de 2 por 2.
Então, o que é
uma matriz quadrada?
Mesma quantidade de linhas é
a mesma quantidade de colunas.
E ela precisa ser não singular.
Ser não singular significa que
o determinante dela não pode ser 0.
Então, basicamente, inverter
uma matriz, no Python,
a gente pode fazer utilizando
o módulo do NumPy de linear algebra,
então "np.linalg",
e do linalg, eu vou usar
o método "inv", de inverter.
Então, vou criar uma variável
matriz inversa
e vou atribuir
para ela a inversa,
o cálculo da inversa dessa matriz
quadrada que a gente criou.
Quando a gente inverte
uma matriz, o que significa?
É como se eu estivesse
fazendo um cálculo análogo
a 1 sobre um determinado
número, esse é o inverso dela.
Então, 1 sobre
essa matriz
é a mesma coisa que a inversa
dessa matriz, perfeito?
Então, se a gente
rodar esse código,
a gente vai ver, aqui, a matriz
quadrada que a gente tinha criado
e, aqui, a inversa
dessa matriz quadrada.
Ela é uma matriz quadrada,
então ela pode ser invertível
e ela é não singular, então ela
pode ser invertível também,
tem que obedecer esses
dois critérios aí, perfeito?
E aí, a gente pode,
depois, no nosso código
verificar essa inversão
dessa matriz.
Como eu poderia
fazer isso?
Se eu fizer o produto escalar
da matriz original com a sua inversa,
eu tenho que ter como resultado
a matriz identidade, ou seja,
a matriz identidade
no contexto de álgebra
é como se fosse o valor 1.
Então, é como se eu multiplicasse
a matriz por 1 sobre a matriz,
eu vou ter 1 como resposta, é isso
que quer dizer esse código aqui.
Seguindo no nosso código,
como a gente falou
de determinante da matriz,
a gente pode verificar, aqui,
como calcular o determinante
de uma matriz.
Então, vou criar,
aqui, uma matriz
para a gente calcular
o determinante com base nela
e eu vou utilizar o mesmo módulo
de linear algebra do NumPy,
só que eu vou usar, agora,
o método "det", de determinante.
Então, qual é o determinante
dessa matriz?
Se a gente rodar
esse código,
a gente vai ver que o determinante
dessa matriz é 4.99.
Se eu tiver
uma matriz singular,
o que significa
uma matriz singular?
Significa que é uma matriz
cujo determinante é 0.
Se o determinante dela é 0, eu
não consigo inverter essa matriz.
Então, essa matriz
singular que a gente criou
mais embaixo do nosso código é
uma matriz que não teria inversa,
não seria possível calcular
a inversa dessa matriz, perfeito?
Outra operação extremamente
importante é a norma de um vetor.
Então, a norma de um vetor
é como a gente calcula,
de certa forma,
o tamanho desse vetor.
Então, a norma desse vetor...
Existem várias formas de se
calcular a norma de um vetor.
A mais conhecida e a mais
utilizada é a norma L2,
que é também conhecida
como a norma Euclidiana.
Então, ela é dada
por essa continha aqui,
então vai ser a raiz quadrada
da soma dos valores dessa matriz,
desse vetor, ao quadrado,
então a raiz quadrada da soma
dos quadrados dos valores
dessa matriz, perfeito?
Então, se a gente tiver
aqui uma matriz 3 e -4,
a norma Euclidiana
dessa matriz vai ser 5.
Como eu chego
nesse cálculo?
Fazendo a raiz quadrada
da soma dos quadrados
dos valores desse
vetor, perfeito?
Bom, então, para calcular
a norma Euclidiana,
eu posso usar o módulo
de álgebra linear do NumPy,
utilizando o método
"norm", certo?
E eu vou ter a norma
Euclidiana.
Existem, também, outras formas
de normas que a gente pode utilizar,
por exemplo, a norma
de Frobenius.
Não é uma norma
muito utilizada,
mas apenas para a gente conseguir
visualizar que existe um parâmetro,
eu posso parametrizar o tipo
de norma que eu quero
na função, no método
"norm" do NumPy.
Então, por padrão, essa biblioteca
já utiliza a norma Euclidiana,
mas eu poderia definir outra
dentre as normas possíveis,
e, para saber
as normas possíveis,
a gente pode olhar na documentação
dessa biblioteca, perfeito?
Outro cálculo muito interessante,
muito importante para criar IAs
e para a álgebra linear
como um todo,
é você calcular
distâncias entre vetores.
Então, eu posso
ter dois vetores
e eu quero calcular o quão
distantes esses vetores são.
Para fazer a distância
entre vetores,
eu posso, simplesmente,
calcular a diferença entre eles.
Então, se eu calcular
a diferença entre eles,
eu vou ter um
menos o outro,
eu vou ter a distância de cada
vetor em cada dimensão.
Quando eu quero criar, utilizar
uma distância Euclidiana,
o que é a distância Euclidiana?
É uma distância baseada
na norma Euclidiana,
então vai ser a norma L2,
a norma Euclidiana da diferença
entre os dois vetores,
então eu vou fazer a raiz quadrada
das diferenças elevado ao quadrado,
essa que é a conta.
No Python, eu poderia,
simplesmente, fazer a norma
das diferenças dos vetores
que a gente calculou aqui.
E, aqui, eu coloco a ordem 2,
a ordem já é presumida que é o 2,
por default, e aí,
a gente tem, aqui,
o que a gente chama de distância
Euclidiana entre dois vetores,
o quão distantes esses
dois vetores estão, certo?
Outra métrica bastante interessante
para se calcular entre dois vetores
é a distância de Manhattan.
Então, a única diferença
entre a distância Euclidiana
e a distância de Manhattan
é a ordem dessa norma.
Então, uma a gente
chama de norma L2,
a distância Euclidiana,
a distância de Manhattan,
a gente chama de L1,
tudo por causa da norma,
da ordem da norma.
O que isso muda
na prática?
Na norma euclidiana,
isso muda que a gente usa
o quadrado das diferenças.
Na norma de Manhattan,
na distância de Manhattan,
norma L1, a gente
usa o módulo,
como se estivesse
usando uma ordem 1,
elevado a 1, esse número, então
a gente usa a norma dos valores.
Então, se a gente verificar aqui,
a gente vai ter diferentes normas
e cada norma dessa vai ser
mais útil ou menos útil
dependendo do caso
de uso que a gente tiver.
Outras operações úteis aqui
no contexto de álgebra linear:
a gente pode transpor uma matriz,
então, frequentemente,
a gente vai ter que calcular
a transposta de uma matriz.
O que é a transposta
de uma matriz?
É simplesmente eu transformar
o que era linha em coluna
e o que era coluna em linha.
Então, se eu criar uma matriz
3 por 2 com esses valores,
com esses elementos,
a transposta dessa matriz
original vai ser, simplesmente,
essa matriz transposta, ou seja,
linhas vão virar colunas
e colunas vão virar linhas, então,
no final, a dimensão dessa matriz
vai de 3 por 2
para 2 por 3.
Então, vamos rodar
esse código para visualizar.
Eu tinha uma matriz
original 3 por 2,
a matriz transposta
vai ser 2 por 3.
Outro conceito interessante é
você criar uma matriz identidade.
Então, a matriz identidade é
uma matriz similar a um número 1,
então aquela matriz que, se você
multiplicar uma matriz por ela,
é como se você estivesse
multiplicando um número por 1,
então vai dar ela mesma.
Então, a matriz identidade
no Python, utilizando NumPy,
a gente usa a função "eye".
Se eu colocar aqui,
como entrada, 2,
eu vou ter uma matriz
identidade 2 por 2,
se eu colocar 3, eu vou ter
uma matriz identidade 3 por 3.
Então, matriz identidade sempre
são matrizes quadradas, ou seja,
mesmo número
de linhas e de colunas.
Então, se a gente rodar aqui,
a gente vai ter a matriz
identidade, ela é 2 por 2,
vou ter a matriz transposta,
e, se eu multiplicar uma matriz
por sua identidade,
se eu fizer o produto
escalar entre eles,
eu vou ter como resultado
a mesma matriz original, certo?
Olha, "[1. 2.] [3. 4.]", resposta
"[1. 2.] [3. 4.]", perfeito?
Agora, algumas funções auxiliares
que o NumPy oferece para a gente:
então o NumPy oferece,
por exemplo,
uma funçãozinha
que é interessante
para o que a gente vai
fazer durante as aulas,
que se chama "linspace".
O que esse linspace faz?
Ele simplesmente cria pontos
linearmente, igualmente espaçados,
vamos dizer assim.
Então, o que essa funçãozinha
aqui está fazendo?
Eu vou criar 5 pontos
entre 0 e 10,
e esses pontos vão ser
linearmente espaçados,
então 0, 2.5, 5, 7.5, 10.
Tenho 5 pontos e a distância
entre eles é igual.
Outras funções auxiliares:
eu posso criar uma matriz
só com zeros no Python,
utilizando a função
"np.zeros".
E, aqui, eu posso passar
como entrada,
como parâmetro
para essa função,
a dimensão da matriz
que eu quero criar.
Então, eu vou criar
uma matriz de zeros
com dimensões
de 2 linhas e 3 colunas.
Da mesma forma, eu posso criar
uma matriz de números 1s,
utilizando "np.ones".
Vou passar aqui, eu quero criar
uma matriz de 3 linhas e 2 colunas
só com números uns.
Então, está aqui a nossa matriz
de zeros e a nossa matriz de uns.
E a gente pode, também,
concatenar esses vetores,
ou seja, eu posso juntar dois
vetores, ou juntar duas matrizes.
Então, aqui, por exemplo, eu tenho
uma matriz chamada "array1"
e essa matriz é "np.array",
primeira linha, segunda linha,
cada uma com duas colunas.
E eu posso ter um vetor,
uma matriz que, na verdade,
aqui, vai ter uma linha só,
uma linha e duas colunas.
Então, se eu concatenar esses dois
arrays aqui, esses dois vetores,
essas duas matrizes,
aqui no caso,
eu vou ter, na verdade,
no final, uma matriz,
esse array concatenado
vai adicionar essa linha
nesse vetor original
que tinha só duas linhas.
Então, vou ter um vetor
de 3 linhas e 2 colunas,
uma matriz, no caso,
de 3 linhas e 2 colunas.
Esse parâmetro aqui,
"axis", significa:
quando é 0, eu vou
adicionar nas linhas,
quando são colunas, eu
vou adicionar nas colunas,
então um eu vou adicionar embaixo,
o outro eu vou adicionar ao lado,
é como se fosse isso.
Então, aqui, eu tenho um exemplo
tanto adicionando linhas
a uma matriz original,
quanto adicionando colunas
a uma matriz original.
Então, se a gente
rodar esse exemplo,
eu tenho a matriz 1,
2 por 2,
e a matriz 2, que é uma linha
por duas colunas.
Quando eu concateno elas
utilizando o eixo 0, o axis 0,
eu vou adicionar
uma linha a mais.
No outro exemplo,
no debaixo,
eu tenho uma matriz
de duas linhas e uma coluna,
e eu vou concatenar
um array nas colunas,
então eu vou adicionar
duas colunas diferentes.
Então, eu continuo com o 7 e 8,
vou adicionar o 1, 3, 2, 4 aqui,
como colunas novas
nessa matriz original.
E, por último,
para fechar o nosso lab,
eu também posso utilizar uma função
de reshape nos meus dados,
nos meus arrays, nos meus
vetores e matrizes.
O que o reshape vai fazer?
Eu consigo alterar o formato
dessa matriz ou desse array.
Então, imagina que eu tenho
uma matriz, um array, na verdade,
que eu tenho 6 elementos
nesse array.
Eu posso fazer um reshape
nesse dado, nessa matriz,
e transformá-lo em uma matriz
de duas linhas e três colunas.
O único ponto importante
para a gente pensar é:
no final, essa conta
tem que fechar,
então, se eu tenho 6 colunas,
6 números, 6 elementos nesse vetor,
e eu quero transformá-lo
em uma matriz,
as dimensões, aqui,
têm que multiplicar
e dar o mesmo resultado
que a quantidade de elementos
desse vetor original.
Então, se eu tenho 6 elementos,
2 vezes 3 tem que dar 6.
Eu poderia, também,
fazer 3 por 2, ou 6 por 1,
mas eu não poderia fazer
2 por 4, porque daria erro.
Então, se a gente rodar
aqui, tem o array original,
e esse array, quando eu fizer
o reshape de 2 por 3 dele,
ele vai reajustar
esses elementos
em uma matriz de duas
linhas e três colunas.
Se eu colocasse
4 aqui, daria erro.
Por que daria erro?
Porque eu só tenho
6 elementos,
para fazer um reshape de 2 e 4,
eu teria que ter 8 elementos.
Então, o único ponto
de atenção seria esse.
Então, agora você já sabe
muito sobre IA generativa
e já deu seus primeiros
passos sobre álgebra linear
para conseguir,
você mesmo,
implementar os seus próprios
modelos de a generativa.