Curso
Grande parte do que você precisa saber para realmente mergulhar em machine learning é álgebra linear — e é exatamente isso que este tutorial aborda. No post de hoje, vamos passar pelos tópicos de álgebra linear que você precisa entender para aprimorar sua intuição sobre como e quando os métodos de machine learning funcionam, olhando no nível de vetores e matrizes.
Ao final do tutorial, esperamos que você se sinta mais confiante para analisar um algoritmo de perto!
Conteúdo
Introdução
Um dos três pilares que você precisa dominar se quer aprender ciência de dados é machine learning. E, quando você realmente começa a entrar em ML, é importante ir além da superfície para construir intuição e entender como os algoritmos funcionam.
E acho que você, cientista de dados (em formação), vai concordar: chegar lá pode ser desafiador.
Mas não precisa ser.
SciPy versus NumPy
No tutorial de NumPy da DataCamp, você viu que essa é uma das bibliotecas centrais para computação científica em Python. Ela reúne ferramentas e técnicas para resolver no computador modelos matemáticos de problemas em Ciência e Engenharia. Mas o recurso que você mais vai usar é o objeto array multidimensional de alta performance: uma estrutura poderosa que permite calcular com eficiência arrays e matrizes.
Agora, o SciPy é basicamente o NumPy.
Também é um dos pacotes fundamentais de computação científica, oferecendo algoritmos matemáticos e funções utilitárias, mas ele é construído em cima da extensão NumPy do Python. Isso significa que SciPy e NumPy costumam ser usados em conjunto!
Mais adiante, você vai ver como a colaboração entre essas duas bibliotecas é natural.
Interaja com NumPy e SciPy
Para trabalhar bem com ambos os pacotes, primeiro você precisa conhecer o básico da biblioteca e da sua estrutura de dados poderosa. Para operar com esses arrays, existe uma grande quantidade de funções matemáticas de alto nível que atuam sobre matrizes e arrays.
A seguir, você confere um panorama rápido dos tópicos que precisa dominar para usar o SciPy com eficiência. Em essência, é fundamental entender a estrutura de array, como lidar com tipos de dados e como manipular a forma (shape) dos arrays. Se isso parece desconhecido, considere fazer o Python NumPy Tutorial da DataCamp e, enquanto estuda, consulte também o cheat sheet de NumPy.
Se você já sabe tudo isso, pule para “Álgebra linear com SciPy”, mas deixe à mão o cheat sheet de SciPy para álgebra linear!
O essencial sobre objetos ndarray do NumPy
Estruturalmente, um array é um conjunto de ponteiros: uma combinação de endereço de memória, tipo de dado, shape e strides. Ele guarda informações sobre os dados brutos, como localizar um elemento e como interpretá-lo.
Endereço de memória e strides importam quando você aprofunda nos detalhes de baixo nível; já tipo de dado e shape são coisas que iniciantes precisam conhecer e entender. Outros dois atributos úteis são data e size, que permitem obter ainda mais informações sobre o array.
Relembre o uso dos atributos de ndarray no bloco DataCamp Light a seguir. O array myArray já foi carregado. Você pode inspecioná-lo digitando-o no shell IPython e pressionando ENTER.
Você verá nos resultados que o tipo de dado de myArray é int64. Ao trabalhar intensamente com arrays, você certamente vai lembrar que dá para converter o tipo de dados com o método astype().
Mesmo assim, quando usa SciPy e NumPy juntos, as seguintes funções de tratamento de tipos do NumPy podem ser bem úteis, especialmente com números complexos:
Tente adicionar print() para ver os resultados. Assim você percebe que números complexos têm parte real e imaginária. As funções np.real() e np.imag() retornam, respectivamente, essas partes.
Alternativamente, você pode usar np.cast para converter um array para outro tipo, como float no exemplo acima.
O único ponto mais “diferente” no bloco acima é np.real_if_close(). Ao receber um input complexo, como myArray, ela retorna um array real se as partes complexas forem próximas de zero. Esse “próximo de 0” pode ser ajustado com o argumento tol. Brinque com esse limite para ver o que acontece!
Criação de arrays
Você já viu como inspecionar seu array e ajustar o tipo de dado, mas ainda não viu explicitamente como criar arrays. Você já sabe que pode usar np.array() para isso, mas existem outras rotinas de criação que vale conhecer: np.eye() e np.identity().
A função np.eye() cria uma matriz quadrada com dimensão igual ao inteiro positivo passado como argumento. As entradas são preenchidas com zeros, exceto a diagonal principal, que recebe 1. A função np.identity() funciona de forma semelhante, retornando uma matriz identidade.
Atenção: np.eye() aceita o argumento adicional k para especificar o índice da diagonal a ser preenchida com 1s.
Outras funções para criação de arrays que ajudam muito quando você trabalha com matrizes em álgebra linear:
np.arange()cria um array com valores igualmente espaçados entre dois números. Você define o passo entre os elementos,- O mesmo vale para
np.linspace(), mas aqui você define a quantidade de elementos desejada no array. - Por fim,
np.logspace()também cria valores igualmente espaçados, porém em escala logarítmica (base 10).
Essas funções criam arrays em forma de malhas, que podem ser usados para gerar meshgrids — assunto mencionado antes neste post.
Observação: numpy já está importado como np no bloco abaixo!
Agora que você refrescou a memória sobre tipos de dados, é hora de falar de indexação e fatiamento.
Indexação e fatiamento
Com indexação, você usa colchetes [] para acessar valores do array. Em outras palavras, indexar é acessar um subconjunto de elementos. Fatiar (slicing) é parecido, mas um pouco mais avançado: ao fatiar, você trabalha com “regiões” de dados em vez de pontos específicos.
Relembre com os exemplos abaixo:
Com os conceitos frescos, vale conhecer alguns truques de indexação para ganhar eficiência em computação científica com SciPy. Quatro funções úteis: np.mgrid(), np.ogrid(), np.r e np.c.
As duas últimas são comuns para quem já mexe com NumPy. np.r e np.c são usadas para empilhar arrays por linha ou por coluna, respectivamente. Com elas, você constrói arrays rapidamente em vez de usar np.concatenate().
Ao olhar as duas primeiras, você pode se perguntar por que precisa de um meshgrid. Eles geram dois arrays contendo as coordenadas x e y em cada posição de uma grade retangular. np.meshgrid() recebe dois arrays 1D e produz duas matrizes 2D correspondentes a todos os pares (x, y) dos arrays.
np.mgrid()é uma implementação do meshgrid do MATLAB e retorna arrays com o mesmo shape. Ou seja, as dimensões e o número de arrays de saída são iguais ao número de dimensões de indexação.np.ogrid(), por outro lado, gera um meshgrid aberto e menos denso do que onp.mgrid(). A diferença visual aparece no bloco acima.
Você pode ler mais sobre as diferenças entre as duas funções aqui.
Outra função útil para indexação/fatiamento é np.select(). Ela retorna valores de uma lista de arrays conforme condições que você define no primeiro argumento. No segundo argumento, você passa o array considerado para a seleção.
Veja o exemplo:
Perfeito! Agora que você selecionou os valores certos do array original, ainda pode ajustar o shape e manipular o novo array.
Seleção e manipulação de shape
O NumPy oferece várias formas de selecionar e manipular o shape dos arrays — você provavelmente já conhece muitas. A seguir, um resumo rápido de funções úteis.
Quando se trata de SciPy, o mais útil é conseguir achatar arrays, empilhar e dividir arrays. Você já viu np.c e np.r, que muitas vezes substituem np.concatenate(), mas há outras que vale conhecer!
Como np.hstack() para empilhar horizontalmente e np.vstack() para empilhar verticalmente. Da mesma forma, use np.vsplit() e np.hsplit() para dividir vertical e horizontalmente. Mas você provavelmente já sabe disso.
Prove no bloco abaixo:
Lembrete: np.eye() cria um array identidade 2x2, perfeito para empilhar com o array 2D carregado no bloco acima.
Se quiser saber mais sobre as condições para empilhar arrays, vá aqui. Porém, você também pode olhar os arrays e o que as funções fazem para criar uma intuição das “regras” ao juntar por linhas ou colunas.
Ao dividir arrays, o ponto mais importante é o shape, para escolher o índice correto da divisão.
Além de empilhar e dividir, vale lembrar de funções que garantem trabalhar com arrays de certas dimensões — indispensáveis ao aprofundar na computação científica.
Considere as funções abaixo:
Nota: há diferença entre “reshape” e “resize”. No primeiro, você muda a forma dos dados sem alterar os valores; no segundo, os dados podem mudar dependendo do novo shape.
Além de dividir/empilhar e manipular arrays, existe a “vetorização” a considerar. Quando você aplica uma função a um array, normalmente ela age elemento a elemento. Por exemplo, np.cos(), np.sin() e np.tan(). Quando isso acontece, dizemos que a função é vetorizada.
Mas, ao definir suas próprias funções — o que você provavelmente fará — você pode querer vetorizá-las. Para isso, use np.vectorize():
Entre outras funções vetorizadas úteis, considere np.angle() para obter o ângulo dos elementos de arrays complexos, além das trigonométricas, exponenciais e logarítmicas básicas.
Álgebra linear com SciPy
Agora que você sabe o que precisa para tirar proveito de ambos os pacotes, é hora de entrar no tema central: álgebra linear.
Mas antes de ver como usar Python, garanta que seu ambiente está pronto!
Instalar SciPy
Claro, primeiro certifique-se de ter o Python instalado. Vá para esta página se ainda precisar fazer isso :) Se estiver no Windows, adicione o Python à variável de ambiente PATH (environment variable). Além disso, não esqueça de instalar um gerenciador de pacotes, como o pip, para usar as bibliotecas open source do Python.
Versões recentes do Python 3 já vêm com o pip. Verifique se você tem e, se tiver, atualize antes de instalar outros pacotes:
pip install pip --upgrade pip --version
Mas só instalar um gerenciador não basta; você também precisa baixar o wheel da biblioteca: acesse aqui para obter o wheel do SciPy. Depois do download, abra o terminal na pasta do arquivo e instale. Em seguida, confira se a instalação deu certo e qual versão está rodando:
# Install the wheel install "scipy‑0.18.1‑cp36‑cp36m‑win_amd64.whl"# Confirm successful installimport scipy# Check package versionscipy.__version__
Depois desses passos, você está pronto para começar!
Dica: instale o pacote baixando a distribuição Anaconda do Python. É um jeito rápido de começar, pois a Anaconda inclui 100 dos pacotes mais populares de Python, R e Scala para ciência de dados, além de ambientes como Jupyter e Spyder. Se quiser começar com Jupyter Notebook, confira este tutorial de Jupyter.
Se ainda não baixou, acesse aqui para obter.
Vetores e matrizes: o básico
Com o ambiente pronto, vamos finalmente começar com álgebra linear em Python. Essencialmente, essa disciplina estuda espaços vetoriais e as transformações lineares entre eles. Essas transformações podem ser descritas com matrizes, o que facilita os cálculos.
Lembre: um espaço vetorial é um conceito fundamental. É um espaço onde você tem uma coleção de objetos (vetores) e pode somá-los ou escalá-los sem que o vetor resultante saia do espaço. Lembre também que vetores são linhas (ou colunas) de uma matriz.
Como isso funciona em Python?
Você cria um vetor facilmente com np.array(). Da mesma forma, você dá estrutura de matriz a qualquer ndarray 1D ou 2D com np.matrix() ou np.mat().
Teste no bloco abaixo:
Então arrays e matrizes são iguais, tirando a formatação?
Não exatamente. Há diferenças:
- Uma matriz é 2D, enquanto arrays geralmente são
nD, - Como as funções acima sugerem, matrix é uma subclasse de
ndarray, - Arrays e matrizes têm
.T(), mas só matrizes têm.H()e.I(), - Multiplicação de matrizes difere da multiplicação elemento a elemento de arrays, e
- A operação
**produz resultados diferentes para matrizes e arrays
Ao trabalhar com matrizes, você pode ter algumas em que a maioria dos elementos é zero. Essas são “matrizes esparsas”. Já as com muitos elementos não nulos são “densas”.
Isso pode parecer trivial, mas no SciPy isso impacta os módulos que você usará. De forma geral, use scipy.linalg para matrizes densas; para esparsas, veja scipy.sparse, que também tem seu próprio scipy.sparse.linalg.
Para matrizes esparsas, há várias opções de criação. O bloco abaixo lista algumas:
Além dessas, há outras: Block Sparse Row com bsr_matrix(), formato COOrdinate com coo_matrix(), armazenamento DIAgonal com dia_matrix() e lista encadeada por linha com lil_matrix().
São muitas opções, mas qual escolher ao criar sua própria matriz esparsa?
Não é difícil.
Basicamente, pense primeiro em como vai inicializar. Depois, no que você quer fazer com a matriz.
Checklist para decidir o tipo:
- Para preencher elemento a elemento, use
coo_matrix()oudok_matrix(). - Para inicializar com um array na diagonal, use
dia_matrix(). - Para matrizes baseadas em fatias, use
lil_matrix(). - Para construir a partir de blocos menores, considere
bsr_matrix(). - Para acesso rápido a linhas e colunas, converta com
csr_matrix()ecsc_matrix(), respectivamente. Não são ideais para inicializar, mas aceleram multiplicações.
Simples assim!
Operações com vetores
Agora que você revisou vetores, matrizes densas e esparsas, é hora de olhar de perto as operações com vetores. O foco aqui é em operações matemáticas para você perceber semelhanças e diferenças com matrizes — afinal, grande parte da álgebra linear envolve matrizes.
Você já viu como criar um vetor com np.array(). Com os vetores em mãos, veja algumas operações básicas. vector1 e vector2 já estão carregados no bloco abaixo:
Agora que você viu operações com vetores, vamos para o trabalho com matrizes!
Matrizes: operações e rotinas
Assim como no início da seção anterior, você já sabe criar matrizes, mas ainda não viu como usá-las a seu favor. Aqui vai um panorama de funções e rotinas básicas para trabalhar com eficiência.
Primeiro, algumas funções. Se você já usou NumPy, elas serão familiares; se não, vai ver que são fáceis de começar.
Exemplos: np.add() e np.subtract() para somar e subtrair arrays ou matrizes; np.divide() e np.multiply para divisão e multiplicação. Nada misterioso, certo? A função np.dot(), vista antes para produto escalar, também funciona com matrizes — passando duas matrizes.
Básico, né?
Vamos além. Em multiplicações, considere também np.vdot() para produto escalar de vetores, np.inner() e np.outer() para produtos interno e externo de arrays, np.tensordot() e np.kron() para o produto de Kronecker de dois arrays:
Dica: adicione prints para ver o resultado de cada produto.
Além disso, considere funções do módulo linalg: exponencial de matriz linalg.expm(), linalg.expm2() e linalg.expm3(). A diferença está no método de cálculo. Fique com a primeira como padrão, mas teste as três para comparar!
Também há funções trigonométricas como linalg.cosm(), linalg.sinm() e linalg.tanm(), suas versões hiperbólicas linalg.coshm(), linalg.sinhm(), linalg.tanhm(), a função sinal linalg.signm(), o logaritmo matricial linalg.logm() e a raiz quadrada matricial linalg.sqrtm().
Você também pode avaliar uma função de matriz com linalg.funm(). Veja o exemplo:
Você passa a matriz como primeiro argumento e a função (neste caso, uma lambda) como segundo. A função passada para linalg.funm() precisa ser vetorizada.
Agora, algumas rotinas básicas. Os principais atributos da matriz: T para transposição, H para transposição conjugada, I para inversa e A para converter em array.
Teste-os:
Ao transpor, você cria uma nova matriz cujas linhas são as colunas da original. A transposição conjugada intercambia os índices de linha e coluna e conjuga os elementos. A inversa é a matriz que, multiplicada pela original, resulta na identidade.
Além desses atributos, existem funções: np.transpose() e linalg.inv() para transposição e inversa, respectivamente.
Você também pode obter o traço (soma dos elementos na diagonal principal) com np.trace(). E recuperar o posto (rank) da matriz — número de valores singulares acima de um limite — com linalg.matrix_rank do NumPy.
Não se preocupe se o conceito de rank ainda não estiver claro; veremos mais adiante.
Por ora, foque em mais duas rotinas:
- A norma de uma matriz, com
linalg.norm, indica um tamanho associado às entradas da matriz e pode trazer informações úteis sobre sua “magnitude”. - O determinante, com
linalg.det(), reduz uma matriz quadrada a um único número, útil para saber se a matriz é inversível.
Por fim, resolver grandes sistemas lineares é uma das aplicações mais básicas de matrizes. Para um sistema \(Ax = b\), em que \(A\) é quadrada e \(b\) é um vetor/matriz, você tem duas abordagens, dependendo do tipo de matriz:
Para matrizes esparsas, use linalg.spsolve(). Se não for possível resolver exatamente, ainda pode obter uma solução aproximada de \(x\) com linalg.lstsq().
Dica: não perca o cheat sheet de SciPy da DataCamp.
Agora que você já sabe criar e operar com matrizes, vamos para tópicos mais avançados essenciais para machine learning.
Autovalores e autovetores
O primeiro tema são autovalores e autovetores.
Autovalores oferecem uma nova forma de enxergar o “coração” de uma matriz. Antes, vamos explicar autovetores. Quase todos os vetores mudam de direção ao serem multiplicados por uma matriz. Porém, certos vetores resultantes ficam na mesma direção do vetor original após a multiplicação. Esses são os autovetores.
Em outras palavras, ao multiplicar um autovetor por uma matriz, o vetor resultante é igual ao autovetor original multiplicado por \(\lambda\), o autovalor: \[Ax = \lambda x. \]
Isso significa que o autovalor traz informação valiosa: ele indica se um autovetor é esticado, contraído, invertido ou permanece inalterado quando multiplicado por uma matriz.
Você usa eig() do módulo linalg do SciPy para resolver problemas de autovalores/autovetores (comuns ou generalizados) em matrizes quadradas.
Observe que eigvals() é outra forma de obter apenas os autovalores.
Com matrizes esparsas, use scipy.sparse para as funções corretas de autovalores/autovetores:
la, v = sparse.linalg.eigs(myMatrix,1)
Nota: no código acima, especificamos a quantidade de autovalores/autovetores a recuperar — no caso, 1.
Autovalores e autovetores são essenciais em várias técnicas de visão computacional e machine learning, como Análise de Componentes Principais (PCA) para redução de dimensionalidade e EigenFaces para reconhecimento facial.
Decomposição em valores singulares (SVD)
Em seguida, você precisa conhecer SVD para realmente aprender ciência de dados. A decomposição em valores singulares de uma matriz \(A\) é a fatoração de \(A\) no produto de três matrizes: \(A = U * \Sigma * V^t\).
Os tamanhos individuais são os seguintes, se \(A\) tem dimensão \(M\) x \(N\):
- \(U\): \(M\) x \(M\)
- \(V\): \(N\) x \(N\)
- \(\Sigma\): \(M\) x \(N\)
O \(*\) indica multiplicação e o \(^t\) em \(V^t\) indica transposição (linhas e colunas trocadas).
Em termos simples, a SVD decompõe a matriz em partes mais simples e significativas, que podem carregar informações de interesse.
Nota: para matrizes esparsas, use sparse.linalg.svds() para fazer a decomposição.
Se você é novo em ciência de dados, a decomposição matricial pode parecer abstrata. Mas a SVD é útil em muitas tarefas, como compressão de dados, redução de ruído e análise. A seguir, veja como usá-la para comprimir imagens:
# Import the necessary packagesimport numpy as npfrom scipy import linalgfrom skimage import dataimport matplotlib.pyplot as plt# Get an image from `skimage`img= data.camera()# Check number of singular valueslinalg.svdvals(img)# Singular Value DecompositionU, s, Vh = linalg.svd(img)# Use only 32 singular valuesA = np.dot(U[:,0:32], np.dot(np.diag(s[0:32]), Vh[0:32,:]))fig = plt.figure(figsize=(8, 3))# Add a subplot to the figureax = fig.add_subplot(121)# Plot `img` on grayscaleax.imshow(img, cmap='gray')# Add a second subplot to the figureax2 = fig.add_subplot(122)# Plot `A` in the second subplotax2.imshow(A)# Add a titlefig.suptitle('Image Compression with SVD', fontsize=14, fontweight='bold')# Show the plotplt.show()
O resultado será algo assim:

Considere também estes exemplos de uso da SVD:
- A SVD é intimamente ligada à Análise de Componentes Principais (PCA), usada para redução de dimensionalidade: ambas resultam em “novos eixos” construídos a partir de combinações lineares dos eixos do espaço de atributos. Esses “novos eixos” decompõem a variância dos dados de acordo com a contribuição de cada direção. Para um exemplo prático de PCA, confira nosso tutorial de Scikit-Learn.
- Outro elo está em data mining e processamento de linguagem natural (PLN): Latent Semantic Indexing (LSI). É uma técnica usada em recuperação de documentos e similaridade entre palavras. O LSI usa SVD para agrupar documentos por conceitos formados por diferentes palavras encontradas neles, reduzindo correlações ruidosas entre palavras e documentos e diminuindo a dimensionalidade original.
Perceba como a SVD é um conceito importante na sua jornada em ciência de dados. Vale ir além do que este tutorial cobre: por exemplo, visite esta página para se aprofundar.
O que vem a seguir?
Você chegou ao fim do tutorial! A partir daqui, o caminho é com você.
Mas espera aí!
Não perca cursos que vão mais fundo em álgebra linear: este tutorial foi só uma introdução e não cobriu tudo!
Considere também fazer o tutorial de Machine Learning da DataCamp, que vai enriquecer seu programa de estudos depois deste tutorial de SciPy sobre álgebra linear. Se quiser voltar ao básico, veja nosso tutorial de NumPy ou o curso Intermediate Python for Data Science.
