Tanquem el mòdul 6 amb una observació: hem escrit sis mòduls de codi i, sense dir-ho, hem pres decisions d'eina a cadascun. Python pur per als grafs i les regles, scikit-learn i PyTorch per als models, i mencions de passada a Prolog, Lisp, CLIPS o Drools que no vam instal·lar mai. Aquesta lliçó ordena aquesta primera decisió, la del llenguatge, que és la que condiciona totes les altres. Veurem què li demana la intel·ligència artificial a un llenguatge de programació, per què Python s'ha convertit en la seva llengua franca malgrat ser lent (i com es compensa aquesta lentitud, amb un micro-benchmark que executaràs), què van aportar i continuen aportant els llenguatges històrics de la IA simbòlica (Lisp i Prolog), quin paper tenen R i Julia al món científic, i quins llenguatges apareixen quan un model surt del portàtil i entra en producció: Java i Scala, C++, JavaScript, Rust, Go i, sobretot, SQL, perquè les dades de NovaMarket no viuen en un CSV sinó en una base de dades. Acabarem amb la idea que articula la indústria, "entrenar en Python, servir en X", amb els formats d'intercanvi que la fan possible, i amb la decisió concreta per al taller de la Marta. És important perquè triar malament el llenguatge no es nota al prototip, sinó sis mesos després, quan l'equip intenta mantenir-lo, integrar-lo o accelerar-lo. Les llibreries concretes es tracten a 07-03 i els entorns a 07-04; aquí només parlem de llenguatges.

Contingut

  1. Què li demana la IA a un llenguatge
  2. Python: per què domina
  3. El punt feble de Python i com es compensa: micro-benchmark
  4. Els llenguatges històrics de la IA simbòlica: Lisp i Prolog
  5. R i Julia: els llenguatges del món científic
  6. Llenguatges de producció i desplegament
  7. SQL: les dades viuen en bases de dades
  8. "Entrenar en Python, servir en X": formats d'intercanvi
  9. Taula comparativa de llenguatges
  10. La decisió per a NovaMarket
  11. Errors Comuns i Consells
  12. Exercicis
  13. Conclusió

  1. Què li demana la IA a un llenguatge

Un programa d'IA no és un programa qualsevol. Mira què hem fet al llarg del curs: explorar grafs i arbres de joc (mòdul 3), transformar taules de milers de files i entrenar models que multipliquen matrius milions de vegades (mòduls 4 i 5), representar regles i fets i raonar-hi (mòdul 6), i en tots els casos experimentar: provar, mesurar, canviar un paràmetre i tornar a provar. D'aquí surten cinc exigències:

Exigència Què significa On l'hem sentit
Expressivitat Escriure la idea amb poc codi i llegir-la després; estructures de dades riques (llistes, diccionaris, tuples) integrades al llenguatge El motor de regles de 06-02 cabia en 60 línies; heapq i deque a 03-02
Ecosistema numèric Arrays, àlgebra lineal, estadística, optimització, gràfics, ja fets i provats NumPy, scikit-learn, PyTorch als mòduls 4 i 5
Rendiment (via llibreries natives) Que el càlcul pesant corri a velocitat de C o de GPU encara que el llenguatge sigui lent L'MLP de 05-03 va entrenar 300 èpoques en segons perquè PyTorch està escrit en C++
Interoperabilitat Parlar amb bases de dades, API web, fitxers de tota mena, altres llenguatges L'informe amb csv de 02-03; a 07-03 veurem FastAPI
Comunitat i talent Documentació, exemples, respostes als fòrums, gent a qui contractar Cada error que vas buscar a Internet mentre seguies el curs

Cap llenguatge guanya en tot. La història dels llenguatges en IA és la història de quina d'aquestes exigències pesava més a cada època: als anys 60 i 70 l'expressivitat simbòlica (Lisp, Prolog); als 2000 l'estadística (R); avui l'ecosistema numèric i la comunitat (Python), amb el rendiment delegat en llibreries natives i amb llenguatges de sistemes per servir en producció.

  1. Python: per què domina

Python va néixer el 1991 com a llenguatge de propòsit general, no pensat per a IA. El seu domini actual s'explica per la suma de quatre factors:

  • Sintaxi llegible i dinàmica. S'escriu gairebé com pseudocodi (compara encadenar_endavant de 06-01 amb la seva descripció en prosa), no exigeix declarar tipus ni compilar, i el bucle "escric, executo, miro" dura segons. Per experimentar és el que importa.
  • Ecosistema. Sobre NumPy (2006, hereu de Numeric de 1995) es va construir tota la resta: SciPy, pandas, Matplotlib, scikit-learn, i després Theano, TensorFlow i PyTorch. Quan el deep learning va enlairar-se el 2012, Python ja tenia les eines i la comunitat científica que R no tenia en xarxes ni Lisp en números.
  • És un llenguatge "cola". Python és fàcil d'estendre amb codi en C, C++ o CUDA (el llenguatge de les GPU de NVIDIA). NumPy, PyTorch, scikit-learn són en realitat embolcalls en Python de biblioteques natives: tu escrius a @ b i qui multiplica és una rutina BLAS en C o Fortran; escrius xarxa(xb) i qui calcula és el nucli C++ de PyTorch. Python hi posa la interfície còmoda; el metall l'hi posa un altre.
  • Notebooks. Jupyter (07-04) va fer de Python l'entorn natural per a l'anàlisi exploratòria: codi, resultats i gràfics al mateix document.

A més, Python és el llenguatge que ja saben els enginyers de dades, els administradors de sistemes i els desenvolupadors web, cosa que redueix la fricció perquè un equip com el de NovaMarket incorpori IA sense canviar de llenguatge.

  1. El punt feble de Python i com es compensa: micro-benchmark

Python és lent per al càlcul numèric escrit en Python. Dues raons:

  • És interpretat i dinàmic. Cada total += x dins d'un bucle implica comprovar el tipus de total i de x, buscar l'operació de suma per a aquests tipus, crear un objecte float nou... desenes d'instruccions de màquina per cada suma que en C en seria una.
  • El GIL (Global Interpreter Lock): a l'intèrpret estàndard només un fil executa codi Python alhora, així que els fils no acceleren el càlcul pur (sí l'espera de xarxa o de disc). Des de fa poc existeixen versions experimentals de l'intèrpret sense GIL, però la pràctica habitual continua sent una altra: treure el càlcul de l'intèrpret.

La compensació es diu vectorització: en lloc d'un bucle Python que suma un milió de números un a un, es crida una funció nativa que recorre les dades en C. Comprova-ho:

import time, random
import numpy as np

N = 1_000_000
random.seed(42)
llista_a = [random.random() for _ in range(N)]     # un milió de floats en una llista Python
llista_b = [random.random() for _ in range(N)]

def cronometrar(nom, funcio, repeticions=3):
    """Executa funcio() diverses vegades i mostra el millor temps en mil·lisegons."""
    millor = float("inf")
    for _ in range(repeticions):
        inici = time.perf_counter()
        resultat = funcio()
        millor = min(millor, time.perf_counter() - inici)
    print(f"{nom:38s} {millor*1000:9.2f} ms   resultat={resultat:.4f}")
    return millor

# 1) Suma d'un milió de números
def suma_bucle():
    total = 0.0
    for x in llista_a:
        total += x
    return total

def suma_builtin():
    return sum(llista_a)

arr_a = np.array(llista_a)                         # còpia a un ndarray de float64
arr_b = np.array(llista_b)
def suma_numpy():
    return arr_a.sum()

# 2) Producte escalar: sum(a[i] * b[i])
def producte_bucle():
    total = 0.0
    for x, y in zip(llista_a, llista_b):
        total += x * y
    return total

def producte_numpy():
    return float(arr_a @ arr_b)

t1 = cronometrar("suma: bucle for en Python", suma_bucle)
t2 = cronometrar("suma: sum() de Python", suma_builtin)
t3 = cronometrar("suma: numpy .sum()", suma_numpy)
t4 = cronometrar("producte escalar: bucle for + zip", producte_bucle)
t5 = cronometrar("producte escalar: numpy a @ b", producte_numpy)
print(f"acceleracio suma: x{t1/t3:.0f}   acceleracio producte: x{t4/t5:.0f}")

Sortida a l'equip del curs (els temps absoluts variaran al teu; les proporcions, poc):

suma: bucle for en Python                  15.35 ms   resultat=500014.5439
suma: sum() de Python                       5.69 ms   resultat=500014.5439
suma: numpy .sum()                          0.33 ms   resultat=500014.5439
producte escalar: bucle for + zip          29.34 ms   resultat=249910.5634
producte escalar: numpy a @ b               0.39 ms   resultat=249910.5634
acceleracio suma: x47   acceleracio producte: x75

Explicació, línia a línia del que importa:

  • time.perf_counter() és el rellotge d'alta resolució adequat per mesurar intervals; prenem el millor de tres per reduir el soroll del sistema operatiu.
  • suma_bucle és Python pur: uns 15 ms per a un milió de sumes. sum() és una funció incorporada escrita en C, però continua recorrent objectes Python un a un: 3 vegades més ràpida. arr_a.sum() recorre un bloc contigu de números de 8 bytes amb instruccions vectorials del processador: unes 45 vegades més ràpida que el bucle.
  • El producte escalar amplia la bretxa (unes 70 vegades): el bucle Python fa dues operacions per element i @ delega en BLAS, la biblioteca d'àlgebra lineal optimitzada que també fan servir scikit-learn i PyTorch.
  • El resultat és idèntic en totes les variants; només canvia qui fa la feina. Amb matrius en lloc de vectors, o en GPU, la diferència arriba a milers de vegades.

D'aquí surt la regla d'or del Python científic, que desenvoluparem a 07-02: no escriguis bucles sobre números; escriu operacions sobre arrays. I quan alguna cosa no es pugui vectoritzar, existeixen les extensions natives (Cython, Numba, o directament C++ enllaçat amb pybind11) i els compiladors JIT; formen part del mateix patró: Python organitza, el codi natiu calcula.

  1. Els llenguatges històrics de la IA simbòlica: Lisp i Prolog

A 01-01 vam situar el naixement de la IA als anys 50 amb John McCarthy, i a 06-01 vam veure que la lògica va ser el seu primer llenguatge formal. Cadascuna d'aquestes dues tradicions va tenir el seu llenguatge de programació.

4.1 Lisp: llistes i codi com a dades

Lisp (McCarthy, 1958) és el segon llenguatge d'alt nivell més antic que continua en ús. La seva idea central: el programa s'escriu amb la mateixa estructura de dades que manipula, la llista entre parèntesis. Una funció és una llista, una dada és una llista, i una funció pot construir una altra funció i executar-la. Això el va fer ideal per a la IA simbòlica dels anys 60-80: processament de símbols, arbres de cerca, regles que es generen i es transformen. Exemple il·lustratiu (no s'executa a l'entorn del curs) d'una funció que decideix si una comanda és dins del termini de desistiment i de com el codi és una dada:

;; Definició: (defun nom (paràmetres) cos). Tot són llistes entre parèntesis.
(defun dins-termini (dies) (<= dies 14))

(dins-termini 9)                    ; => T   (vertader)
(mapcar #'dins-termini '(9 20 3))   ; => (T NIL T): aplica la funció a cada element

;; Codi com a dades: una llista que ÉS una expressió, avaluada després amb eval
(defvar regla '(<= 9 14))           ; la cometa evita avaluar-la ara
(eval regla)                        ; => T

defun defineix funcions, mapcar aplica una funció a una llista (el nostre map de Python), i eval executa una llista com si fos codi: per això els sistemes experts dels 80 s'escrivien en Lisp, i de Lisp hereten Python i JavaScript idees com les funcions de primera classe, lambda, la recollida d'escombraries i el bucle interactiu llegir-avaluar-imprimir (REPL) que avui és la consola de Python. Els seus dialectes actuals (Common Lisp, Scheme, Clojure sobre la JVM) tenen nínxols, però per a IA el seu paper avui és històric.

4.2 Prolog: fets, regles i consultes

Prolog (Colmerauer i Kowalski, 1972) és la lògica de primer ordre convertida en llenguatge: el programa són fets i regles en forma de clàusules de Horn (06-01), i executar és consultar: l'intèrpret busca una demostració per encadenament cap enrere amb unificació i retrocés, exactament l'algorisme demostrar que vam escriure a 06-01. Va ser el llenguatge del projecte japonès de la Cinquena Generació (01-01). Les set regles de devolució de 06-01, il·lustratives en Prolog:

% Fets de la comanda 48213 (cafetera NovaBrew): lliurada fa 9 dies, amb etiqueta, sense usar
dies_des_lliurament(p48213, 9).
etiqueta_original(p48213).
sense_usar(p48213).
% Fets de la comanda 48377 (auriculars NovaSound): 400 dies, defectuós
dies_des_lliurament(p48377, 400).
defectuos(p48377).

% Regles R1-R7 de 06-01. ":-" es llegeix "si"; la coma és "i"; les majúscules són variables
dins_termini(P)                :- dies_des_lliurament(P, D), D =< 14.          % R1
en_garantia(P)                 :- dies_des_lliurament(P, D), D =< 1095.        % R2
desistiment_ok(P)              :- dins_termini(P), etiqueta_original(P), sense_usar(P).  % R3
garantia_ok(P)                 :- defectuos(P), en_garantia(P).                % R4
procedeix_reemborsament(P)     :- desistiment_ok(P).                           % R5
procedeix_reemborsament(P)     :- garantia_ok(P).                              % R6
enviament_devolucio_gratis(P)  :- garantia_ok(P).                              % R7

% Consultes (el que es tecleja a l'intèrpret i el que respon):
% ?- procedeix_reemborsament(p48213).       -> true.   (per R1, R3, R5)
% ?- procedeix_reemborsament(p48377).       -> true.   (per R2, R4, R6)
% ?- enviament_devolucio_gratis(p48213).    -> false.  (no és defectuós)
% ?- procedeix_reemborsament(X).            -> X = p48213 ; X = p48377.

Fixa't en l'última consulta: X és una variable i Prolog retorna totes les comandes per a les quals pot demostrar l'objectiu; en Python vam haver d'escriure el bucle nosaltres. Prolog continua viu en nínxols (motors de regles, anàlisi del llenguatge, verificació, planificació) i la seva descendència són els motors de regles de 06-02/06-04 (CLIPS, Drools, experta) i les bases de dades deductives (Datalog). El que no té és ecosistema numèric ni comunitat d'ML: no entrenaries la regressió logística de 04-04 en Prolog.

  1. R i Julia: els llenguatges del món científic

5.1 R: l'estadística com a llenguatge

R (1993) va néixer d'S, el llenguatge dels estadístics de Bell Labs, i continua sent el llenguatge de referència en estadística acadèmica, bioestadística i ciència de dades "clàssica": té el model lineal, els tests, els gràfics i milers de paquets estadístics (CRAN) que en Python de vegades no existeixen o arriben tard. El tidyverse (dplyr, ggplot2) és la referència de manipulació de dades i de gràfics per gramàtica. Exemple il·lustratiu (no executable a l'entorn del curs): la regressió que el Diego voldria per saber si l'import i el lliurament expliquen la devolució.

# Il·lustratiu: R llegeix comandes.csv i ajusta una regressió logística amb la fórmula
comandes <- read.csv("comandes.csv")
model <- glm(retornat ~ import_comanda + dies_lliurament + client_nou + categoria,
             data = comandes, family = binomial)
summary(model)           # coeficients, errors estàndard, p-valors, en una taula

La sintaxi de fórmula (y ~ x1 + x2) i el summary amb inferència estadística són el que R fa millor que ningú: a scikit-learn (04-04) vam tenir coeficients però no p-valors, perquè scikit-learn està pensat per predir, no per explicar estadísticament. R perd davant de Python en deep learning, en desplegament i en integració amb sistemes; els equips mixtos solen tenir els estadístics en R i els enginyers en Python, i es parlen a través de CSV, bases de dades o paquets pont.

5.2 Julia: velocitat de C amb sintaxi científica

Julia (2012) es va dissenyar per resoldre el "problema dels dos llenguatges": prototipar en Python i reescriure en C++ per a producció. Compila just a temps (JIT) a codi natiu, així que un bucle escrit en Julia corre a velocitat de C sense vectoritzar, i la seva sintaxi està pensada per a matemàtiques (índexs des d'1, operadors unicode, broadcasting amb punt). Il·lustratiu:

# Il·lustratiu: la suma i el producte escalar del benchmark, en Julia. Un bucle és ràpid.
a = rand(1_000_000); b = rand(1_000_000)
function producte(a, b)
    total = 0.0
    for i in eachindex(a)
        total += a[i] * b[i]
    end
    return total
end
producte(a, b)      # el bucle compilat corre a velocitat nativa
sum(a .* b)         # versió vectoritzada: el punt aplica * element a element

Julia té una comunitat forta en computació científica, equacions diferencials i optimització, i llibreries d'ML (Flux, MLJ), però un ecosistema molt menor que Python i menys demanda a les empreses. És l'opció a vigilar si el coll d'ampolla del teu projecte és un algorisme numèric propi que no cap ni a NumPy ni a PyTorch.

  1. Llenguatges de producció i desplegament

Un model entrenat s'ha de servir: respondre en mil·lisegons dins del web de NovaMarket, executar-se al mòbil del repartidor o córrer en un cluster sobre terabytes de comandes. Aquí apareixen altres llenguatges, cadascun amb el seu paper:

  • Java i Scala. L'ecosistema empresarial (bancs, telecomunicacions, grans botigues) corre a la JVM. Apache Spark, el motor de dades a escala, està escrit en Scala i es fa servir des de Scala, Java o Python (PySpark). Un model entrenat en Python pot executar-se dins d'una aplicació Java a través d'ONNX (secció 8) o de llibreries Java d'ML (Deeplearning4j, Tribuo), o simplement cridant un servei Python per HTTP.
  • C++. El llenguatge dels motors: PyTorch, TensorFlow, ONNX Runtime, OpenCV i els nuclis de NumPy estan escrits en C++ (i CUDA). Quan la inferència ha d'anar a màxima velocitat o encastar-se en un dispositiu (una càmera del magatzem de Zaragoza comptant caixes), es carrega el model des de C++ amb libtorch (l'API C++ de PyTorch) o ONNX Runtime. Gairebé ningú entrena en C++; molts serveixen en C++ sense saber-ho.
  • JavaScript / TypeScript. El llenguatge del navegador. TensorFlow.js i ONNX Runtime Web executen models al client (sense enviar dades al servidor: útil per a la privacitat, 02-04) i Node.js serveix API. Un recomanador lleuger de NovaMarket podria reordenar productes a la mateixa pàgina.
  • Rust i Go. Llenguatges de sistemes moderns, segurs i ràpids, per a serveis: servidors d'inferència, eines de dades (Polars, el DataFrame ràpid que veurem a 07-03, està escrit en Rust; molts tokenitzadors de LLM també). Go domina en infraestructura (Docker i Kubernetes són en Go). No s'hi entrena; s'hi serveix i s'hi orquestra.
  • Kotlin/Swift per a mòbils amb models exportats (TensorFlow Lite, Core ML), i CUDA/Triton per a qui escriu nuclis de GPU. Mencions per al mapa; fora de l'abast del curs.

  1. SQL: les dades viuen en bases de dades

El llenguatge que que ha de saber tot l'equip de la Marta, a més de Python, és SQL. Les comandes reals de NovaMarket no són a comandes.csv: són a la base de dades transaccional de la botiga i al magatzem de dades (data warehouse). El primer pas de gairebé qualsevol projecte és una consulta SQL que extreu, filtra i agrega. I moltes agregacions són més ràpides i més clares en SQL, a la base de dades, que portant milions de files a pandas.

Exemple executable: carreguem les 3.000 comandes sintètiques de 04-01 en una base de dades SQLite en memòria (Python la porta de sèrie) i calculem la taxa de devolució per categoria, la consulta que el Diego demanaria el primer dia:

import sqlite3
from novamarket_ml import generar_comandes_ml    # el generador del mòdul 4

comandes = generar_comandes_ml(3000, 42)         # DataFrame amb import_comanda, categoria, retornat...
con = sqlite3.connect(":memory:")                # base de dades SQLite temporal en RAM
comandes.to_sql("comandes", con, index=False)    # pandas crea la taula i l'omple

sql = """
SELECT categoria,
       COUNT(*)                            AS comandes,
       SUM(retornat)                       AS retornats,
       ROUND(100.0 * AVG(retornat), 1)     AS taxa_devolucio_pct,
       ROUND(AVG(import_comanda), 2)       AS import_mitja
FROM comandes
WHERE import_comanda > 20                -- descartem comandes minúscules
GROUP BY categoria
ORDER BY taxa_devolucio_pct DESC;
"""
for fila in con.execute(sql):
    print(fila)

Sortida:

('electronica', 1006, 213, 21.2, 126.94)
('informatica', 596, 97, 16.3, 129.18)
('llar', 887, 122, 13.8, 128.16)
('accessoris', 430, 56, 13.0, 130.97)

Lectura: SELECT tria columnes i expressions; COUNT(*) compta files del grup; SUM(retornat) compta retornats perquè la columna val 0/1; AVG(retornat) és directament la taxa (la mitjana d'un 0/1 és una proporció, el mateix truc que vam fer servir a pandas a 04-01); WHERE filtra abans d'agrupar; GROUP BY categoria forma un grup per categoria; ORDER BY ... DESC ordena de més gran a més petit. L'electrònica retorna el 21 % i els accessoris el 13 %, coherent amb la "veritat oculta" del generador (el terme +1.0 * (categoria == "electronica")). A 07-02 farem exactament aquesta operació amb groupby de pandas per comparar.

SQL és declaratiu (dius què vols, no com calcular-ho, com Prolog), és el mateix a PostgreSQL, MySQL, SQL Server, BigQuery o Snowflake amb petites variants, i és la frontera natural entre l'equip de dades i el d'IA: la majoria de les característiques de 04-03 (comandes_previes, taxa_devolucio_previa) es poden calcular amb SQL al magatzem de dades i servir-les ja llestes al model.

  1. "Entrenar en Python, servir en X": formats d'intercanvi

La conseqüència de les seccions 6 i 7 és una arquitectura que es repeteix a gairebé totes les empreses:

flowchart LR
    BD[(Base de dades<br/>SQL)] -->|consulta / extracció| PY["Python<br/>pandas + scikit-learn / PyTorch<br/>exploració i entrenament"]
    PY -->|joblib / pickle| SRV1["Servei Python<br/>API HTTP FastAPI"]
    PY -->|exportació ONNX| ONNX[Fitxer .onnx]
    PY -->|state_dict / TorchScript| SRV1
    ONNX --> JAVA["Aplicació Java<br/>ONNX Runtime"]
    ONNX --> CPP["Motor C++ / dispositiu<br/>ONNX Runtime, libtorch"]
    ONNX --> WEB["Navegador / mòbil<br/>ONNX Runtime Web, TF.js"]
    SRV1 --> BOTIGA[Web de NovaMarket]
    JAVA --> BOTIGA
    CPP --> MAG["Càmeres / magatzem"]
    WEB --> BOTIGA

Els formats d'intercanvi són la peça que permet canviar de llenguatge sense reentrenar:

Format Què desa Qui el llegeix Riscos i límits
pickle / joblib Qualsevol objecte Python serialitzat (el Pipeline complet de 04-03, amb el seu ColumnTransformer) Només Python, amb les mateixes versions de scikit-learn i dependències Executa codi en carregar-se: no carreguis mai un pickle d'origen no fiable; es trenca en canviar de versió; no el llegeix cap altre llenguatge
state_dict de PyTorch (torch.save) Els tensors de paràmetres d'una xarxa (els 497 números de l'MLP de 05-02), no l'arquitectura Python amb PyTorch; cal reconstruir la xarxa amb el mateix codi abans de carregar Per sota és un pickle: mateixes cauteles de seguretat
ONNX (Open Neural Network Exchange) El graf de càlcul del model en un format estàndard obert ONNX Runtime des de Python, C++, Java, C#, JavaScript, mòbils; conversors des de PyTorch, scikit-learn (skl2onnx), TensorFlow No totes les operacions es converteixen (els passos de preprocessament exòtics solen donar guerra); versions d'operadors
SavedModel / TFLite / Core ML Models TensorFlow, per a mòbil (TFLite) o Apple (Core ML) Els seus ecosistemes Lligats a un proveïdor
PMML Models estadístics clàssics en XML Eines empresarials antigues, Java Antiquat, poc expressiu

A 07-03 executarem el desament i la recàrrega amb joblib i state_dict i comprovarem que el model recarregat prediu el mateix. Aquí n'hi ha prou amb retenir la regla: desar un model és desar codi + pesos + versions; el format que triïs determina des de quin llenguatge el podràs fer servir i quant de temps continuarà carregant.

  1. Taula comparativa de llenguatges

Llenguatge Paradigma Fortalesa en IA Ecosistema IA Quan triar-lo
Python Multiparadigma, dinàmic, interpretat Prototipatge, entrenament, ciència de dades, LLM El més gran amb diferència (NumPy, pandas, scikit-learn, PyTorch, Hugging Face...) Per defecte per a gairebé tot el que hem fet al curs
Lisp Funcional, simbòlic, codi = dades IA simbòlica històrica, metaprogramació Reduït avui Interès històric; nínxols en Clojure/Common Lisp
Prolog Lògic, declaratiu Regles, unificació, cerca amb retrocés Petit, especialitzat Quan el problema és lògica pura (06-01); avui sol preferir-se un motor de regles encastat
R Funcional/vectorial, estadístic Estadística, inferència, gràfics, informes Molt gran en estadística (CRAN, tidyverse); menor en DL Anàlisi estadística rigorosa, equips d'estadístics, bioestadística
Julia Multiparadigma, compilat JIT Càlcul científic ràpid sense dos llenguatges Mitjà i creixent Algorismes numèrics propis on el bucle és el coll d'ampolla
Java / Scala Orientat a objectes / funcional, JVM Integració empresarial, Spark, serveis Gran en dades (Spark, Kafka), mitjà en ML Servir models dins de sistemes Java existents; dades a escala
C++ Imperatiu/OO, compilat Motors, inferència de màxima velocitat, encastat És el nucli de gairebé tot (PyTorch, ONNX Runtime, OpenCV) Rendiment extrem, dispositius, escriure el propi motor
JavaScript / TS Multiparadigma, dinàmic Inferència al navegador i a Node, interfícies TF.js, ONNX Runtime Web Models al client, demos web, privacitat
Rust / Go Sistemes, compilats, segurs Serveis d'inferència, eines de dades, infraestructura Creixent (Polars, tokenitzadors) / infraestructura Serveis d'alt rendiment i fiabilitat
SQL Declaratiu, relacional Extracció, agregació, característiques al magatzem de dades Universal a les bases de dades Sempre que les dades siguin en una base de dades, és a dir, gairebé sempre

  1. La decisió per a NovaMarket

La Marta reuneix l'equip (dues analistes, un desenvolupador backend en Java, ella mateixa) i el Diego pregunta el de sempre: "quant costa i en què ens fiquem?". La decisió que en surt, i que sostindrà el mòdul 8:

  • Python per explorar, prototipar i entrenar. És el que sap l'equip de dades, el que hem fet servir als mòduls 3-6 i on hi ha les llibreries (07-03). Amb regles d'estil: vectoritzar (07-02), i no reescriure en cap altre llenguatge fins que un perfil demostri que cal.
  • SQL per extreure i agregar. Les consultes de la secció 7 es fan al magatzem de dades; a Python hi arriba només el necessari, ja agregat quan es pugui. Les característiques que es puguin calcular en SQL (historial del client) es calculen allà, perquè entrenament i producció facin servir la mateixa definició.
  • Servir en Python amb una API HTTP (FastAPI, 07-03) que el web de NovaMarket crida: el predictor de devolucions, el recomanador i l'assistent comencen així, perquè és el més barat de construir i mantenir amb l'equip actual.
  • ONNX a la recambra si el desenvolupador Java necessita executar un model dins de la plataforma de la botiga sense dependre d'un servei Python, o si algun dia una càmera del magatzem ha d'executar el classificador de 05-04 en local.
  • Res de Prolog, Lisp, R ni Julia en producció: es coneixen, es respecten, i les idees de Prolog viuen al motor de regles Python de 06-02 (o en un motor de regles empresarial si creix).

Amb aquesta decisió, el que queda per aprendre és com es fa servir bé Python per a dades: NumPy, pandas i Matplotlib, les llibreries que al mòdul 4 vam utilitzar sense explicar-les. És la propera lliçó.

Errors Comuns i Consells

  • "Python és lent, així que reescric en C++". Gairebé mai. El 95 % de les vegades el problema és un bucle Python sobre números que es vectoritza en una línia de NumPy (secció 3) o una consulta que s'hauria d'haver fet en SQL. Mesura abans (perfilador o time.perf_counter) i reescriu només el 5 % calent.
  • Confondre el llenguatge amb la llibreria. "PyTorch és ràpid" no vol dir "Python és ràpid"; vol dir que estàs cridant C++ des de Python. Si fiques un bucle Python dins del pas d'entrenament, tornes a la lentitud.
  • Carregar pickles de qualsevol lloc. Un .pkl o un .pt descarregat pot executar codi arbitrari en obrir-se. Només models d'origen fiable; per compartir amb tercers, prefereix formats que no executin codi (ONNX, safetensors).
  • Triar el llenguatge per moda o per currículum. Julia o Rust són excel·lents, però si l'equip sap Python i el coll d'ampolla no existeix, introduir-los afegeix cost de manteniment sense benefici: és la pregunta del Diego.
  • Oblidar SQL. L'error més freqüent en perfils que vénen de la IA acadèmica: carregar tota la taula a pandas i filtrar-hi el que la base de dades hauria filtrat en mil·lisegons. Aprèn SELECT, WHERE, GROUP BY, JOIN amb la mateixa serietat que fit/predict.
  • Ignorar les versions. El pickle del pipeline entrenat avui pot no carregar amb la propera versió de scikit-learn. Apunta les versions (07-04) al costat del model.

Exercicis

Exercici 1. Amplia el micro-benchmark de la secció 3 amb una tercera operació: la mitjana dels quadrats, sum(x*x for x in llista) / N davant de (arr ** 2).mean(). Abans d'executar, prediu si l'acceleració serà més gran o més petita que la de la suma simple, i per què. Executa i compara.

Exercici 2. Escriu (sense executar, és Prolog il·lustratiu) les dues regles que faltaven de l'exercici 3 de 06-01: la regla R8 "producte d'higiene obert no és retornable" i un fet per a un raspall NovaSmile obert. Afegeix una consulta i la seva resposta esperada. Després respon: quin problema de 06-01 (conflicte entre R8 i R3/R5) continua existint a la versió Prolog i per què el llenguatge no el resol tot sol?

Exercici 3. Modifica la consulta SQL de la secció 7 per obtenir la taxa de devolució per categoria i per si el client és nou, ordenada per categoria i després per taxa, i només per a grups amb almenys 100 comandes (pista: HAVING, que filtra després d'agrupar). Executa-la i interpreta la fila amb més taxa a la llum de la "veritat oculta" del generador de 04-01.

Solucions

Solució 1. Afegint a l'script:

def quadrats_bucle():
    return sum(x * x for x in llista_a) / N
def quadrats_numpy():
    return float((arr_a ** 2).mean())
t6 = cronometrar("mitjana de quadrats: generador Python", quadrats_bucle)
t7 = cronometrar("mitjana de quadrats: numpy (a**2).mean()", quadrats_numpy)
print(f"acceleracio quadrats: x{t6/t7:.0f}")

A l'equip del curs: uns 28,6 ms per al generador davant d'uns 0,87 ms per a NumPy (acceleració d'unes 33 vegades, resultat 0,3332 en tots dos, que és la mitjana teòrica de x² per a x uniforme a [0, 1], 1/3). La predicció raonable era "del mateix ordre que la suma, potser una mica menor": el bucle Python fa ara dues operacions per element (multiplicar i sumar), però NumPy també fa dues passades (** 2 crea un array temporal d'un milió de floats i .mean() el recorre), així que l'avantatge es reparteix i queda una mica per sota del de la suma simple. Tot i així, desenes de vegades. La lliçó: la bretxa no depèn de l'operació concreta sinó de qui executa el bucle; i que les expressions NumPy encadenades creen arrays temporals, un cost que a 07-02 aprendrem a tenir en compte.

Solució 2.

higiene(p50001).
obert(p50001).
dies_des_lliurament(p50001, 5).
etiqueta_original(p50001).
sense_usar(p50001).
no_retornable(P) :- higiene(P), obert(P).                   % R8
% ?- no_retornable(p50001).             -> true.
% ?- procedeix_reemborsament(p50001).   -> true.   (per R1, R3, R5: es continua derivant!)

Prolog deriva totes dues coses, igual que el nostre motor de 06-01: les clàusules de Horn no tenen prioritat ni negació a les premisses, i l'intèrpret es limita a demostrar el que se li pregunta. Perquè R8 bloquegi el reemborsament caldria reescriure R3 amb la negació per fallada de Prolog (\+ no_retornable(P)), que és justament la resolució de conflictes per especificitat que vam construir a mà a 06-02. El llenguatge dona el motor; la política de conflictes la continua posant l'enginyer.

Solució 3.

sql = """
SELECT categoria, client_nou,
       COUNT(*)                        AS comandes,
       ROUND(100.0 * AVG(retornat), 1) AS taxa_pct
FROM comandes
GROUP BY categoria, client_nou
HAVING COUNT(*) >= 100
ORDER BY categoria, taxa_pct DESC;
"""
for fila in con.execute(sql):
    print(fila)

Sortida:

('accessoris', 1, 143, 25.9)
('accessoris', 0, 296, 6.8)
('electronica', 1, 316, 41.5)
('electronica', 0, 722, 11.6)
('informatica', 1, 181, 30.4)
('informatica', 0, 434, 9.9)
('llar', 1, 266, 29.7)
('llar', 0, 642, 6.9)

(Hem tret el filtre WHERE import_comanda > 20 per comptar totes les comandes; per això els totals per categoria no coincideixen amb els de la secció 7.) La fila amb més taxa és electrònica + client nou (41,5 %): al generador de 04-01 l'electrònica suma +1,0 al logit i el client nou +1,6, a més del terme creuat nou × import. Tots els grups superen les 100 comandes, així que HAVING no n'elimina cap aquí; amb un filtre de 300 només quedarien els recurrents d'electrònica, llar i informàtica i els nous d'electrònica. Observa que a cada categoria els clients nous retornen entre 3 i 4 vegades més: el mateix senyal que la regressió logística de 04-04 va aprendre tota sola.

Conclusió

Hem ordenat la primera decisió del taller de la Marta. Un llenguatge per a IA ha de ser expressiu, tenir ecosistema numèric, oferir rendiment a través de llibreries natives, interoperar amb dades i sistemes i comptar amb comunitat. Python guanya en quatre de les cinc i compensa la cinquena, la seva lentitud com a intèrpret i el GIL, amb la vectorització (el nostre benchmark: 45-75 vegades més ràpid amb NumPy que amb un bucle) i les extensions natives: Python organitza, C/C++/CUDA calculen. Hem vist d'on venim, Lisp amb el seu codi com a dades i Prolog amb les regles de devolució de 06-01 escrites com a fets, regles i consultes; què aporten R (estadística i inferència) i Julia (velocitat sense dos llenguatges); quins llenguatges esperen el model quan surt del portàtil (Java/Scala i Spark, C++ amb libtorch i ONNX Runtime, JavaScript al navegador, Rust i Go als serveis) i per què SQL és la segona llengua obligatòria de l'equip, amb la taxa de devolució per categoria calculada a la base de dades. L'arquitectura "entrenar en Python, servir en X" es recolza en formats d'intercanvi (pickle/joblib amb els seus riscos, state_dict, ONNX) i la decisió de NovaMarket és clara: Python per prototipar i entrenar, SQL per extreure, servei Python per servir i ONNX a la recambra.

Ara cal aprendre a fer servir bé aquest Python. Als mòduls 4 i 5 vam escriure generar_comandes_ml, groupby, to_datetime, màscares booleanes i corbes amb Matplotlib sense aturar-nos a explicar-los. A 07-02, Python Científic: NumPy, pandas i Matplotlib, obrirem aquestes tres llibreries amb les comandes i la demanda setmanal de NovaMarket: arrays i broadcasting, DataFrames, agrupacions, unions i dates, i gràfics que es descriuen abans de dibuixar-se.

Fonaments d'Intel·ligència Artificial (IA)

Mòdul 1: Introducció a la Intel·ligència Artificial

Mòdul 2: Principis Bàsics de la IA

Mòdul 3: Algorismes en IA

Mòdul 4: Aprenentatge Automàtic (Machine Learning)

Mòdul 5: Xarxes Neuronals i Deep Learning

Mòdul 6: Lògica i Sistemes Experts

Mòdul 7: Eines i Llenguatges de Programació en IA

Mòdul 8: Projectes i Casos d'Estudi

Mòdul 9: Exercicis i Pràctiques

Mòdul 10: Recursos Addicionals

© Copyright 2026. Tots els drets reservats