[{"data":1,"prerenderedAt":444},["ShallowReactive",2],{"lang-switch-post-\u002Fplaylists\u002Fneural-networks\u002Fxor-o-limite-do-perceptron":3,"post-pt-neural-networks-xor-o-limite-do-perceptron":4},"\u002Fen\u002Fplaylists\u002Fneural-networks\u002Fxor-o-limite-do-perceptron",{"id":5,"title":6,"body":7,"cover":428,"date":429,"description":430,"extension":431,"meta":432,"navigation":433,"order":69,"path":434,"playlist":435,"seo":436,"status":437,"stem":438,"tags":439,"__hash__":443},"posts\u002Fpt\u002Fplaylists\u002Fneural-networks\u002Fxor-o-limite-do-perceptron.md","XOR: Onde um Neurônio Só Bate a Cabeça na Parede",{"type":8,"value":9,"toc":419},"minimark",[10,14,19,79,106,110,130,148,166,178,189,193,211,224,237,254,258,265,277,280,284,291,299,303,348,352,360,375,412,415],[11,12,13],"p",{},"Aula 3d, e é a última que existe no repositório até hoje. Antes de fechar essa leva de posts, o professor deixa um gancho que é quase poético: o mesmo perceptron que resolveu tudo até aqui, sem esforço, trava duro num problema de 4 pontos.",[15,16,18],"h2",{"id":17},"o-dataset-portas-lógicas-geometricamente","O dataset: portas lógicas, geometricamente",[20,21,26],"pre",{"className":22,"code":23,"language":24,"meta":25,"style":25},"language-python shiki shiki-themes github-light github-dark","def createLogicalDataset(n=40, func=lambda a, b: bool(a) or bool(b)):\n  X, y = make_blobs(n_samples=n,\n                    centers=[[0.2,0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8]],\n                    n_features=2,\n                    cluster_std=0.05,\n                    center_box=(0,1))\n  y = np.array([func(a>0.5, b>0.5) for a, b in X], dtype=int)\n  return X, y\n","python","",[27,28,29,37,43,49,55,61,67,73],"code",{"__ignoreMap":25},[30,31,34],"span",{"class":32,"line":33},"line",1,[30,35,36],{},"def createLogicalDataset(n=40, func=lambda a, b: bool(a) or bool(b)):\n",[30,38,40],{"class":32,"line":39},2,[30,41,42],{},"  X, y = make_blobs(n_samples=n,\n",[30,44,46],{"class":32,"line":45},3,[30,47,48],{},"                    centers=[[0.2,0.2], [0.8, 0.2], [0.2, 0.8], [0.8, 0.8]],\n",[30,50,52],{"class":32,"line":51},4,[30,53,54],{},"                    n_features=2,\n",[30,56,58],{"class":32,"line":57},5,[30,59,60],{},"                    cluster_std=0.05,\n",[30,62,64],{"class":32,"line":63},6,[30,65,66],{},"                    center_box=(0,1))\n",[30,68,70],{"class":32,"line":69},7,[30,71,72],{},"  y = np.array([func(a>0.5, b>0.5) for a, b in X], dtype=int)\n",[30,74,76],{"class":32,"line":75},8,[30,77,78],{},"  return X, y\n",[11,80,81,86,87,90,91,90,94,97,98,101,102,105],{},[82,83,85],"a",{"href":84},"\u002Fplaylists\u002Fneural-networks\u002Fmulticlasse","O mesmo dataset de 4 blobs do post anterior",", só que agora o rótulo vem de aplicar uma função lógica (",[27,88,89],{},"or",", ",[27,92,93],{},"and",[27,95,96],{},"!=",") em cima de \"esse ponto está à direita de 0.5?\" e \"esse ponto está acima de 0.5?\". Cada blob vira, na prática, um valor de porta lógica: canto inferior-esquerdo é ",[27,99,100],{},"(falso, falso)",", inferior-direito é ",[27,103,104],{},"(verdadeiro, falso)",", e assim por diante.",[15,107,109],{"id":108},"or-e-and-sem-drama","OR e AND: sem drama",[20,111,113],{"className":22,"code":112,"language":24,"meta":25,"style":25},"X, y = createLogicalDataset(func=lambda a, b: bool(a) or bool(b))\nmodel = NeuralNetwork()\nmodel.fit(X, y)\n",[27,114,115,120,125],{"__ignoreMap":25},[30,116,117],{"class":32,"line":33},[30,118,119],{},"X, y = createLogicalDataset(func=lambda a, b: bool(a) or bool(b))\n",[30,121,122],{"class":32,"line":39},[30,123,124],{},"model = NeuralNetwork()\n",[30,126,127],{"class":32,"line":45},[30,128,129],{},"model.fit(X, y)\n",[131,132,133],"blockquote",{},[11,134,135,139,140,143,144,147],{},[136,137,138],"strong",{},"Saída (OR):"," acurácia ",[136,141,142],{},"1.0",", pesos ",[27,145,146],{},"[-1.14, 1.72, 1.57]",".",[20,149,151],{"className":22,"code":150,"language":24,"meta":25,"style":25},"X, y = createLogicalDataset(func=lambda a, b: bool(a) and bool(b))\nmodel = NeuralNetwork()\nmodel.fit(X, y)\n",[27,152,153,158,162],{"__ignoreMap":25},[30,154,155],{"class":32,"line":33},[30,156,157],{},"X, y = createLogicalDataset(func=lambda a, b: bool(a) and bool(b))\n",[30,159,160],{"class":32,"line":39},[30,161,124],{},[30,163,164],{"class":32,"line":45},[30,165,129],{},[131,167,168],{},[11,169,170,139,173,143,175,147],{},[136,171,172],{},"Saída (AND):",[136,174,142],{},[27,176,177],{},"[-2.16, 1.55, 1.79]",[11,179,180,181,184,185,188],{},"As duas portas são linearmente separáveis: em OR, uma única reta separa o blob inferior-esquerdo (o único ",[27,182,183],{},"falso",") dos outros três. Em AND, a mesma coisa, só que isolando o blob superior-direito (o único ",[27,186,187],{},"verdadeiro","). Nada que esse perceptron não tenha feito o post inteiro até aqui.",[15,190,192],{"id":191},"xor-a-mesma-receita-o-mesmo-código-e-trava","XOR: a mesma receita, o mesmo código, e trava",[20,194,196],{"className":22,"code":195,"language":24,"meta":25,"style":25},"X, y = createLogicalDataset(func=lambda a, b: bool(a) != bool(b))\nmodel = NeuralNetwork()\nmodel.fit(X, y)\n",[27,197,198,203,207],{"__ignoreMap":25},[30,199,200],{"class":32,"line":33},[30,201,202],{},"X, y = createLogicalDataset(func=lambda a, b: bool(a) != bool(b))\n",[30,204,205],{"class":32,"line":39},[30,206,124],{},[30,208,209],{"class":32,"line":45},[30,210,129],{},[131,212,213],{},[11,214,215,139,218,143,221,147],{},[136,216,217],{},"Saída (XOR):",[136,219,220],{},"0.5",[27,222,223],{},"[0.006, -0.011, -0.002]",[11,225,226,227,229,230,232,233,236],{},"Acurácia de 0.5 com 2 classes é exatamente o nível de jogar uma moeda. E repara nos pesos: praticamente zero nos três, o modelo essencialmente desistiu, encolhendo o vetor de peso até quase nada em vez de convergir pra qualquer coisa útil. Reproduzi isso eu mesmo, com dataset seedado, e bati na mesma parede: OR e AND em ",[27,228,142],{},", XOR travado em ",[27,231,220],{},", pesos igualmente murchos (",[27,234,235],{},"[-0.025, -0.066, 0.118]",").",[11,238,239,240,242,243,246,247,249,250,253],{},"O motivo é geométrico, e dá pra ver sem fórmula nenhuma: XOR marca como ",[27,241,187],{}," os dois cantos ",[136,244,245],{},"opostos"," na diagonal (inferior-direito e superior-esquerdo) e como ",[27,248,183],{}," os outros dois cantos, também opostos entre si (inferior-esquerdo e superior-direito). Não existe reta nenhuma que separe \"os dois cantos de uma diagonal\" dos \"dois cantos da outra diagonal\": qualquer reta que eu desenhar corta uma das diagonais ao meio, misturando as duas classes dos dois lados. XOR ",[136,251,252],{},"não é linearmente separável",", ponto final, e nenhuma quantidade de treino vai mudar isso, porque o problema não está no algoritmo de aprendizado, está no que um único neurônio, com uma única fronteira reta, consegue representar.",[15,255,257],{"id":256},"interativo-assiste-o-perceptron-nunca-convergir","Interativo: assiste o perceptron nunca convergir",[11,259,260,261,264],{},"Mesmo dataset seedado de cima, agora no XOR. Clica em \"Processar próximo ponto\" várias vezes: repara que, diferente de todo componente anterior nessa playlist, esse aqui ",[136,262,263],{},"nunca"," mostra a mensagem de convergência. Sempre vai sobrar pelo menos um ponto do lado errado, não importa quanto eu clique.",[266,267],"perceptron-explorer",{":classes":268,":points":269,":update-bias":270,":x-max":271,":x-min":272,":y-max":271,":y-min":272,"negative-label":273,"positive-label":274,"x-label":275,"y-label":276},"[1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1]","[[0.6968, 0.1669], [0.7174, 0.2268], [0.785, 0.741], [0.2956, 0.8119], [0.2277, 0.2062], [0.7398, 0.2731], [0.1953, 0.8597], [0.8825, 0.2077], [0.242, 0.791], [0.2, 0.1123], [0.1687, 0.1914], [0.2845, 0.1767], [0.1606, 0.2001], [0.8525, 0.1792], [0.2051, 0.8126], [0.7629, 0.2536], [0.795, 0.885], [0.8166, 0.8368], [0.1879, 0.1273], [0.8749, 0.7859], [0.7797, 0.0856], [0.1146, 0.7098], [0.7403, 0.7475], [0.7904, 0.7111], [0.8883, 0.1835], [0.7816, 0.7047], [0.7808, 0.7555], [0.1283, 0.8251], [0.8054, 0.8719], [0.2016, 0.2204], [0.2135, 0.7738], [0.2253, 0.1869], [0.7806, 0.3015], [0.7977, 0.1275], [0.1934, 0.7845], [0.2509, 0.23], [0.2284, 0.7624], [0.2192, 0.9124], [0.2137, 0.1237], [0.8752, 0.7894]]","true","1","0","falso (XOR)","verdadeiro (XOR)","x0","x1",[11,278,279],{},"Fica à vontade pra clicar bastante. A fronteira vai girar e deslizar sem parar, tentando achar um lugar que não existe.",[15,281,283],{"id":282},"o-que-isso-significa-e-o-que-vem-depois","O que isso significa (e o que vem depois)",[11,285,286,287,290],{},"Esse resultado não é uma curiosidade isolada, é historicamente ",[136,288,289],{},"o"," limite que definiu os primeiros anos de rede neural: um único neurônio, com uma fronteira de decisão linear, tem um teto de expressividade, e XOR fica acima desse teto. A saída, e é aqui que essa playlist ainda não chegou, é empilhar neurônios: um MLP (rede de múltiplas camadas) consegue resolver XOR combinando duas fronteiras lineares numa camada escondida antes de decidir a saída final. Guardei dois papers exatamente pra esse momento, na pasta de referência dessa playlist: a tese de doutorado de Paul Werbos (1974), a derivação mais antiga que se conhece do algoritmo de retropropagação (backpropagation), e o paper de Rumelhart, Hinton e Williams (Nature, 1986), o que de fato popularizou esse algoritmo e destravou o treino prático de MLPs.",[11,292,293,294,298],{},"O repositório do professor ainda não tem esse notebook. As 3 aulas que cobri até aqui (perceptron, Adaline, funções de custo, multiclasse) são tudo que existe publicado até o momento, e o gancho do XOR é exatamente onde a matéria dele também está agora. ",[82,295,297],{"href":296},"\u002Fplaylists\u002Fneural-networks\u002Fmcculloch-pitts-perceptron","Como já falei lá no primeiro post dessa playlist",", essa é uma playlist viva: assim que o professor publicar a aula de MLP e retropropagação, volto aqui pra continuar de onde parei, com Werbos e Rumelhart-Hinton-Williams já esperando pra entrar em cena.",[15,300,302],{"id":301},"fechando","Fechando",[304,305,306,320],"table",{},[307,308,309],"thead",{},[310,311,312,317],"tr",{},[313,314,316],"th",{"align":315},"left","O que eu já sabia",[313,318,319],{"align":315},"O que essa aula assentou",[321,322,323,332,340],"tbody",{},[310,324,325,329],{},[326,327,328],"td",{"align":315},"Perceptron separa qualquer coisa dada tempo suficiente",[326,330,331],{"align":315},"Só separa o que é linearmente separável: o XOR nunca converge, não importa quanto eu treine",[310,333,334,337],{},[326,335,336],{"align":315},"Pesos crescem quando o modelo está errado",[326,338,339],{"align":315},"Em XOR, os pesos encolhem até quase zero, o modelo desiste em vez de continuar tentando algo que não existe",[310,341,342,345],{},[326,343,344],{"align":315},"Rede neural é \"só\" um neurônio com pesos",[326,346,347],{"align":315},"Um neurônio sozinho tem teto de expressividade: empilhar camadas (MLP) é o que existe pra furar esse teto",[15,349,351],{"id":350},"aplicação-prática","Aplicação Prática",[11,353,354,355,359],{},"Não tem um dataset real clássico de \"XOR\" pra buscar (é uma função lógica, não um fenômeno do mundo), então a aplicação prática aqui é mostrar que a mesma barreira de separabilidade linear aparece em dado real, não só em portas lógicas de brinquedo. Uso o dataset de duas luas ",[82,356,358],{"href":357},"\u002Fplaylists\u002Fpattern-recognition\u002Fdbscan-semi-supervised","que já apareceu no Reconhecimento de Padrões",", sabidamente não-linearmente-separável.",[20,361,363],{"className":22,"code":362,"language":24,"meta":25,"style":25},"from sklearn.datasets import make_moons\nX, y = make_moons(n_samples=300, noise=0.05, random_state=42)\n",[27,364,365,370],{"__ignoreMap":25},[30,366,367],{"class":32,"line":33},[30,368,369],{},"from sklearn.datasets import make_moons\n",[30,371,372],{"class":32,"line":39},[30,373,374],{},"X, y = make_moons(n_samples=300, noise=0.05, random_state=42)\n",[304,376,377,388],{},[307,378,379],{},[310,380,381,384],{},[313,382,383],{"align":315},"Modelo",[313,385,387],{"align":386},"right","Acurácia",[321,389,390,398],{},[310,391,392,395],{},[326,393,394],{"align":315},"Perceptron (fronteira linear)",[326,396,397],{"align":386},"0.867",[310,399,400,408],{},[326,401,402,403,407],{"align":315},"KNN (K=5, ",[82,404,406],{"href":405},"\u002Fplaylists\u002Fpattern-recognition\u002Fknn-classifier","já visto no Reconhecimento de Padrões"," como contraste)",[326,409,410],{"align":386},[136,411,142],{},[11,413,414],{},"O perceptron não trava tão feio quanto no XOR (0.867 não é 0.5, porque as duas luas têm uma separação aproximadamente linear na maior parte do espaço, só a curvatura nas pontas engana uma reta), mas fica bem atrás do KNN, que não tem esse teto porque não depende de uma fronteira reta única. É o mesmo limite estrutural do XOR, só que numa versão mais suave: qualquer problema onde a fronteira verdadeira não é uma reta vai expor essa limitação, cedo ou tarde.",[416,417,418],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":25,"searchDepth":39,"depth":39,"links":420},[421,422,423,424,425,426,427],{"id":17,"depth":39,"text":18},{"id":108,"depth":39,"text":109},{"id":191,"depth":39,"text":192},{"id":256,"depth":39,"text":257},{"id":282,"depth":39,"text":283},{"id":301,"depth":39,"text":302},{"id":350,"depth":39,"text":351},null,"2026-08-20","Aula 3d, a última que o professor publicou até agora: o mesmo perceptron que aprende OR e AND sem esforço trava em XOR, preso em 50% de acurácia, pesos praticamente zerados. Fecho essa primeira leva da playlist exatamente onde a matéria de verdade também está: no limiar antes do MLP.","md",{},true,"\u002Fpt\u002Fplaylists\u002Fneural-networks\u002Fxor-o-limite-do-perceptron","neural-networks",{"title":6,"description":430},"published","pt\u002Fplaylists\u002Fneural-networks\u002Fxor-o-limite-do-perceptron",[440,441,442],"xor","separabilidade-linear","mlp","EMbbL6qKtxsH-Yy2xR0WPOX-m5CgV0CYWOpfAuc2COE",1787338984073]