Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Thomas Nield
Matemáticas esenciales para la ciencia de datos
por Thomas Nield
Copyright © 2022 Thomas Nield. Reservados todos los derechos.
Impreso en los Estados Unidos de América.
Publicado por O'Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
Los libros de O'Reilly se pueden comprar con fines educativos, comerciales o de promoción de
ventas. Las ediciones en línea también están disponibles para la mayoría de los títulos (
http://oreilly.com ). Para obtener más información, comuníquese con nuestro departamento de
ventas corporativo/institucional: 800-998-9938 o corporate@oreilly.com.
Editora de adquisiciones: Jessica Haberman
Editora de desarrollo: Jill Leonard
Editora de producción: Kristen Brown
Editor de estilo: Piper Editorial Consulting, LLC
Correctora: Shannon Turlington
Indexador: Potomac Indexing, LLC
Interior Designer: David Futato
Diseño de portada: Karen Montgomery
Ilustrador: Kate Dullea
Junio 2022: Primera Edición
2
responsabilidad por errores u omisiones, incluida, entre otras, la responsabilidad por daños
resultantes del uso o confianza en este trabajo. El uso de la información e instrucciones contenidas
en este trabajo es bajo su propio riesgo. Si alguna muestra de código u otra tecnología que este
trabajo contiene o describe está sujeta a licencias de código abierto o a los derechos de propiedad
intelectual de otros, es su responsabilidad asegurarse de que su uso cumpla con dichas licencias
y/o derechos.
978-1-098-10293-7
[LSI]
Prefacio
En los últimos 10 años más o menos, ha habido un interés creciente en aplicar las matemáticas y la
estadística a nuestro trabajo y vida cotidianos. ¿Porqué es eso? ¿Tiene que ver con el interés
acelerado en la "ciencia de datos", que Harvard Business Review llamó "el trabajo más sexy del
siglo XXI" ? ¿O es la promesa del aprendizaje automático y la "inteligencia artificial" cambiando
nuestras vidas? ¿Es porque los titulares de las noticias están inundados de estudios, encuestas y
hallazgos de investigación, pero no están seguros de cómo analizar tales afirmaciones? ¿O es la
promesa de autos y robots "autónomos" que automatizan trabajos en un futuro cercano?
Argumentaré que las disciplinas de las matemáticas y las estadísticas han captado el interés
general debido a la creciente disponibilidad de datos, y necesitamos las matemáticas, las
estadísticas y el aprendizaje automático para darles sentido. Sí, tenemos herramientas científicas,
aprendizaje automático y otras automatizaciones que nos llaman como sirenas. Confiamos
ciegamente en estas "cajas negras", dispositivos y softwares; no los entendemos, pero los usamos
de todos modos.
Si bien es fácil creer que las computadoras son más inteligentes que nosotros (y esta idea se
comercializa con frecuencia), la realidad no puede ser más opuesta. Esta desconexión puede ser
precaria en muchos niveles. ¿Realmente desea que un algoritmo o una IA ejecuten sentencias
penales o conduzcan un vehículo, pero nadie, incluido el desarrollador, puede explicar por qué se
tomó una decisión específica? La explicabilidad es la próxima frontera de la computación
estadística y la IA. Esto puede comenzar solo cuando abrimos la caja negra y descubrimos las
matemáticas.
También puede preguntar cómo puede un desarrollador no saber cómo funciona su propio
algoritmo. Hablaremos de eso en la segunda mitad del libro cuando discutamos las técnicas de
aprendizaje automático y enfaticemos por qué necesitamos entender las matemáticas detrás de las
cajas negras que construimos.
En otro punto, la razón por la que se recopilan datos a gran escala se debe en gran medida a los
dispositivos conectados y su presencia en nuestra vida cotidiana. Ya no usamos Internet
únicamente en una computadora de escritorio o portátil. Ahora lo llevamos con nosotros en
nuestros teléfonos inteligentes, automóviles y dispositivos domésticos. Esto ha permitido sutilmente
una transición en las últimas dos décadas. Los datos ahora han evolucionado de una herramienta
operativa a algo que se recopila y analiza para objetivos menos definidos. Un reloj inteligente
recopila constantemente datos sobre nuestra frecuencia cardíaca, respiración, distancia recorrida y
otros marcadores. Luego carga esos datos en una nube para analizarlos junto con otros usuarios.
Nuestros hábitos de conducción están siendo recopilados por automóviles computarizados y los
fabricantes los utilizan para recopilar datos y habilitar vehículos autónomos. Incluso los "cepillos de
dientes inteligentes" están llegando a las farmacias, que rastrean los hábitos de cepillado y
almacenan esos datos en una nube. Si los datos del cepillo de dientes inteligente son útiles y
esenciales es otra discusión.
Toda esta recopilación de datos está impregnando todos los rincones de nuestras vidas. Puede ser
abrumador, y se puede escribir un libro completo sobre cuestiones de privacidad y ética. Pero esta
disponibilidad de datos también crea oportunidades para aprovechar las matemáticas y las
estadísticas de nuevas maneras y crear una mayor exposición fuera de los entornos académicos.
Podemos aprender más sobre la experiencia humana, mejorar el diseño y la aplicación de
productos y optimizar las estrategias comerciales. Si comprende las ideas presentadas en este
libro, podrá desbloquear el valor contenido en nuestra infraestructura de almacenamiento de datos.
Esto no implica que los datos y las herramientas estadísticas sean una bala de plata para resolver
todos los problemas del mundo, pero nos han dado nuevas herramientas que podemos usar. A
veces, es igual de valioso reconocer ciertos proyectos de datos como madrigueras de conejo y
darse cuenta de que es mejor gastar los esfuerzos en otra parte.
3
Esta creciente disponibilidad de datos ha abierto el camino para que la ciencia de datos y el
aprendizaje automático se conviertan en profesiones en demanda. Definimos las matemáticas
esenciales como una exposición a la probabilidad, el álgebra lineal, las estadísticas y el aprendizaje
automático. Si está buscando una carrera en ciencia de datos, aprendizaje automático o ingeniería,
estos temas son necesarios. Agregaré las matemáticas, el cálculo y las estadísticas universitarias
necesarias para comprender mejor lo que ocurre en las bibliotecas de caja negra que encontrará.
Con este libro, pretendo exponer a los lectores a diferentes áreas matemáticas, estadísticas y de
aprendizaje automático que serán aplicables a problemas del mundo real. Los primeros cuatro
capítulos cubren conceptos matemáticos fundamentales, incluidos cálculo práctico, probabilidad,
álgebra lineal y estadística. Los últimos tres capítulos darán paso al aprendizaje automático. El
propósito final de enseñar el aprendizaje automático es integrar todo lo que aprendemos y
demostrar conocimientos prácticos en el uso del aprendizaje automático y las bibliotecas
estadísticas más allá de la comprensión de la caja negra.
La única herramienta necesaria para seguir los ejemplos es una computadora con
Windows/Mac/Linux y un entorno Python 3 de su elección. Las bibliotecas principales de Python
que necesitaremos son numpy, scipy, sympy y sklearn. Si no está familiarizado con Python, es un
lenguaje de programación amigable y fácil de usar con recursos de aprendizaje masivos detrás.
Aquí hay algunos que recomiendo:
Ciencia de datos desde cero, 2.ª edición de Joel Grus (O'Reilly)
El segundo capítulo de este libro tiene el mejor curso intensivo de Python que he encontrado.
Incluso si nunca antes ha escrito código, Joel hace un trabajo fantástico al ponerlo en
funcionamiento con Python de manera efectiva en el menor tiempo posible. ¡También es un gran
libro para tener en su estantería y aplicar sus conocimientos matemáticos!
Python para el desarrollador de Java ocupado por Deepak Sarda (Apress)
Si usted es un ingeniero de software que viene de un fondo de programación orientada a objetos y
tipado estáticamente, este es el libro para agarrar. Como alguien que comenzó a programar con
Java, aprecio mucho la forma en que Deepak comparte las características de Python y las
relaciona con los desarrolladores de Java. Si ha trabajado con.NET, C++ u otros lenguajes
similares a C, probablemente también aprenderá Python de manera efectiva con este libro.
Este libro no lo convertirá en un experto ni le dará conocimientos de doctorado. Hago lo mejor que
puedo para evitar expresiones matemáticas llenas de símbolos griegos y en su lugar me esfuerzo
por usar un inglés sencillo en su lugar. Pero lo que hará este libro es que se sienta más cómodo
hablando de matemáticas y estadísticas, brindándole el conocimiento esencial para navegar estas
áreas con éxito. Creo que el camino más amplio hacia el éxito no es tener un conocimiento
profundo y especializado en un tema, sino tener exposición y conocimiento práctico en varios
temas. Ese es el objetivo de este libro, y aprenderá lo suficiente como para ser peligroso y hacer
esas preguntas críticas que alguna vez fueron esquivas.
¡Entonces empecemos!
CONSEJO
Este elemento significa un consejo o sugerencia.
4
NOTA
Este elemento significa una nota general.
ADVERTENCIA
Este elemento indica una advertencia o precaución.
Cómo contactarnos
Dirija sus comentarios y preguntas sobre este libro a la editorial:
O'Reilly Media, Inc.
1005 Carretera Gravenstein Norte
Sebastopol, CA 95472
800-998-9938 (en los Estados Unidos o Canadá)
707-829-0515 (internacional o local)
707-829-0104 (fax)
Tenemos una página web para este libro, donde enumeramos las erratas, ejemplos y cualquier
información adicional. Puede acceder a esta página en https://oreil.ly/essentialMathDataSci.
Envíe un correo electrónico a bookquestions@oreilly.com para comentar o hacer preguntas
técnicas sobre este libro.
Para noticias e información sobre nuestros libros y cursos, visite https://oreilly.com.
EncuénTrain os en LinkedIn: https://linkedin.com/company/oreilly-media
Síguenos en Twitter: https://twitter.com/oreillymedia
Míranos en YouTube: https://youtube.com/oreillymedia
Expresiones de gratitud
Este libro fue el valor de más de un año de esfuerzos de muchas personas. Primero, quiero
agradecer a mi esposa Kimberly por su apoyo mientras escribía este libro, especialmente cuando
criamos a nuestro hijo, Wyatt, hasta su primer cumpleaños. Kimberly es una esposa y madre
increíble, y todo lo que hago ahora es por el futuro mejor de mi hijo y de nuestra familia.
5
Quiero agradecer a mis padres por enseñarme a luchar más allá de mis límites y nunca tirar la
toalla. Dado el tema de este libro, me alegro de que me animaran a tomarme el cálculo en serio en
la escuela secundaria y la universidad, y nadie puede escribir un libro sin salir regularmente de su
zona de confort.
Quiero agradecer al increíble equipo de editores y al personal de O'Reilly que han seguido
abriéndome puertas desde que escribí mi primer libro sobre SQL en 2015. Ha sido maravilloso
trabajar con Jill y Jess para escribir y publicar este libro, y Estoy agradecido de que Jess pensara
en mí cuando surgió este tema.
Quiero agradecer a mis colegas de la Universidad del Sur de California en el programa de
Seguridad y Protección de la Aviación. Haber tenido la oportunidad de ser pionero en conceptos de
seguridad de sistemas de inteligencia artificial me ha enseñado conocimientos que pocas personas
tienen, y espero ver lo que continuaremos logrando en los años venideros. Arch, sigues
sorprendiéndome y me preocupa que el mundo deje de funcionar el día que te jubiles.
Por último, quiero agradecer a mi hermano Dwight Nield y a mi amigo Jon Ostrower, quienes son
socios en mi empresa, Yawman Flight. Arrancar una startup es difícil, y su ayuda ha permitido un
valioso ancho de banda para escribir este libro. Jon me trajo a bordo en la USC y sus incansables
logros en el mundo del periodismo de aviación son nada menos que notables (¡búsquenlo!). Es un
honor que estén tan apasionados como yo por un invento que comencé en mi garaje, y no creo que
pudiera traerlo al mundo sin ellos.
A cualquiera que haya extrañado, gracias por las cosas grandes y pequeñas que ha hecho. La
mayoría de las veces, he sido recompensado por ser curioso y hacer preguntas. No doy eso por
sentado. Como dijo Ted Lasso: “Sé curioso, no crítico”.
Contenido
Las convenciones usadas en este libro...........................................................................................4
Uso de ejemplos de código................................................................................................................5
Aprendizaje en línea de O'Reilly.......................................................................................................5
Cómo contactarnos.............................................................................................................................5
Expresiones de gratitud......................................................................................................................5
Capítulo 1. Repaso de cálculo y matemáticas básicas.........................................................10
Teoría de los números......................................................................................................................10
Orden de operaciones......................................................................................................................11
Variables.............................................................................................................................................12
Funciones...........................................................................................................................................12
sumatorias..........................................................................................................................................15
Exponentes.........................................................................................................................................16
logaritmos...........................................................................................................................................18
Número de Euler y logaritmos naturales.......................................................................................19
Número de Euler............................................................................................................................20
Logaritmos naturales.....................................................................................................................22
Límites..............................................................................................................................................22
Derivados............................................................................................................................................24
Derivadas parciales.......................................................................................................................26
La regla de la cadena....................................................................................................................28
Integrales.......................................................................................................................................29
Conclusión..........................................................................................................................................33
Ejercicios.............................................................................................................................................33
Capítulo 2. Probabilidad................................................................................................................34
comprensión de la probabilidad......................................................................................................34
Probabilidad Versus Estadística..................................................................................................35
matemáticas de probabilidad...........................................................................................................35
Probabilidades conjuntas.............................................................................................................35
6
Unión de probabilidades...............................................................................................................36
Probabilidad Condicional y Teorema de Bayes........................................................................38
Probabilidades Condicionales Conjuntas y Uniones...............................................................39
Distribución binomial.........................................................................................................................40
Distribución beta................................................................................................................................42
Conclusión..........................................................................................................................................47
Ejercicios.............................................................................................................................................47
Capítulo 3. Estadística descriptiva e inferencial....................................................................48
¿Qué son los datos?.........................................................................................................................48
Estadística descriptiva versus estadística inferencial..................................................................49
Poblaciones, muestras y sesgo(bias)............................................................................................49
Estadísticas descriptivas..................................................................................................................52
Media y media ponderada............................................................................................................52
Mediana...........................................................................................................................................53
Moda................................................................................................................................................54
Varianza y desviación estándar...................................................................................................54
La distribución normal...................................................................................................................57
La CDF inversa..............................................................................................................................62
Puntuaciones Z..............................................................................................................................63
Estadística inferencial.......................................................................................................................64
El teorema del límite central.........................................................................................................64
Intervalos de confianza.................................................................................................................66
Comprender los valores P............................................................................................................68
Prueba de hipótesis.......................................................................................................................69
La distribución T: tratar con muestras pequeñas..........................................................................74
Consideraciones sobre Big Data y la falacia del francotirador de Texas.................................75
Conclusión..........................................................................................................................................76
Ejercicios.............................................................................................................................................77
Capítulo 4. Álgebra lineal..............................................................................................................78
¿Qué es un vector?...........................................................................................................................78
Adición y combinación de vectores.............................................................................................81
Vectores de escala........................................................................................................................82
Intervalo y dependencia lineal.....................................................................................................84
Transformaciones lineales...............................................................................................................86
Vectores base.................................................................................................................................86
Multiplicación de vectores de matrices.......................................................................................88
Multiplicación de matrices................................................................................................................92
Determinantes.............................................................................................................................93
Tipos especiales de matrices..........................................................................................................96
Matriz cuadrada.............................................................................................................................96
Matriz de identidad........................................................................................................................96
Matriz inversa.................................................................................................................................96
Matriz diagonal...............................................................................................................................97
matriz triangular.............................................................................................................................97
Matriz dispersa...............................................................................................................................97
Sistemas de Ecuaciones y Matrices Inversas............................................................97
Vectores propios y valores propios...............................................................................................100
Conclusión........................................................................................................................................102
Ejercicios...........................................................................................................................................102
7
Capítulo 5. Regresión lineal.......................................................................................................103
Una regresión lineal básica............................................................................................................103
Residuos y errores cuadráticos.....................................................................................................106
Encontrar la mejor línea de ajuste................................................................................................109
Ecuación de forma cerrada........................................................................................................109
Técnicas de matriz inversa.........................................................................................................110
Descenso de gradiente...............................................................................................................111
Sobreajuste y varianza...................................................................................................................116
Descenso de gradiente estocástico..............................................................................................117
El coeficiente de correlación..........................................................................................................118
Significancia estadística.................................................................................................................120
Coeficiente de determinación........................................................................................................124
Error estándar de la estimación....................................................................................................124
Intervalos de predicción..................................................................................................................125
Train /Divisiones de prueba...........................................................................................................127
Regresión lineal múltiple................................................................................................................131
Conclusión........................................................................................................................................132
Ejercicios...........................................................................................................................................132
Capítulo 6. Regresión logística y clasificación.....................................................................133
Comprender la regresión logística................................................................................................133
Realización de una regresión logística........................................................................................135
Función Logística.........................................................................................................................135
Ajuste de la curva logística.........................................................................................................136
Regresión logística multivariable..................................................................................................140
Comprender las probabilidades de registro................................................................................142
R-cuadrado.......................................................................................................................................145
Valores P...........................................................................................................................................148
Train /Divisiones de prueba...........................................................................................................149
Matrices de confusión.....................................................................................................................150
Teorema y clasificación de Bayes.................................................................................................152
Características del operador del receptor/área bajo la curva..................................................153
Desequilibrio de clases...................................................................................................................154
Conclusión........................................................................................................................................154
Ejercicios...........................................................................................................................................155
Capítulo 7. Redes neuronales....................................................................................................156
Cuándo usar redes neuronales y aprendizaje profundo...........................................................156
Una red neuronal simple................................................................................................................157
Funciones de activación.............................................................................................................159
Propagación hacia adelante.......................................................................................................162
retropropagación..............................................................................................................................166
Cálculo de las derivadas de peso y sesgo..............................................................................166
Descenso de gradiente estocástico..........................................................................................169
Usando scikit-learn..........................................................................................................................171
Limitaciones de las redes neuronales y el aprendizaje profundo............................................172
Conclusión........................................................................................................................................174
Ejercicio.............................................................................................................................................174
Capítulo 8. Asesoramiento profesional y el camino a seguir...........................................175
Redefiniendo la ciencia de datos..................................................................................................175
Una breve historia de la ciencia de datos....................................................................................177
Encontrar su borde..........................................................................................................................178
8
Dominio de SQL...........................................................................................................................179
Competencia en programación.................................................................................................180
Visualización de datos................................................................................................................182
Conociendo su industria.............................................................................................................183
Aprendizaje Productivo...............................................................................................................184
Practicante versus asesor..........................................................................................................184
Qué tener en cuenta en los trabajos de ciencia de datos.........................................................186
Definición de roles.......................................................................................................................186
Enfoque organizacional y compromiso....................................................................................186
Recursos adecuados...................................................................................................................187
Objetivos razonables...................................................................................................................188
Competir con los sistemas existentes......................................................................................189
Un papel no es lo que esperabas.............................................................................................190
¿El trabajo de tus sueños no existe?...........................................................................................191
¿Adónde voy ahora?.......................................................................................................................191
Conclusión........................................................................................................................................192
Uso de renderizado LaTeX con SymPy.......................................................................................193
Distribución binomial desde cero..................................................................................................194
Distribución beta desde cero.........................................................................................................194
Derivación del teorema de Bayes.................................................................................................196
CDF y CDF inversa desde cero....................................................................................................197
Use e para predecir la probabilidad de eventos a lo largo del tiempo....................................198
Escalada de colinas y regresión lineal.........................................................................................198
Escalada de colinas y regresión logística....................................................................................200
Una breve introducción a la programación lineal.......................................................................201
Clasificador MNIST usando scikit-learn.......................................................................................205
Capítulo 1..........................................................................................................................................205
Capitulo 2..........................................................................................................................................206
Capítulo 3..........................................................................................................................................207
Capítulo 4..........................................................................................................................................208
Capítulo 5..........................................................................................................................................209
Capítulo 6..........................................................................................................................................211
Capítulo 7..........................................................................................................................................213
9
Capítulo 1. Repaso de cálculo y matemáticas básicas
Basic Math and Calculus Review Comenzaremos el primer capítulo que cubre qué son los números y
cómo funcionan las variables y funciones en un sistema cartesiano. Luego cubriremos exponentes
y logaritmos. Después de eso, aprenderemos las dos operaciones básicas del cálculo: derivadas e
integrales.
Antes de sumergirnos en las áreas aplicadas de las matemáticas esenciales, como la probabilidad,
el álgebra lineal, las estadísticas y el aprendizaje automático, probablemente deberíamos revisar
algunos conceptos básicos de matemáticas y cálculo. Antes de que dejes caer este libro y corras
gritando, ¡no te preocupes! Presentaré cómo calcular derivadas e integrales para una función de
una manera que probablemente no te enseñaron en la universidad. Tenemos a Python de nuestro
lado, no a lápiz y papel. Incluso si no está familiarizado con las derivadas y las integrales, no
necesita preocuparse.
Haré que estos temas sean lo más precisos y prácticos posible, centrándome solo en lo que nos
ayudará en capítulos posteriores y lo que cae bajo el paraguas de las "matemáticas esenciales".
10
Los números irracionales no se pueden expresar como una fracción. Esto incluye el famoso π, las
raíces cuadradas de ciertos números como 2 y el número e de Euler , del que aprenderemos más
adelante. Estos números tienen un número infinito de dígitos decimales, como
3.141592653589793238462…
Hay una historia interesante detrás de los números irracionales. El matemático griego Pitágoras
creía que todos los números son racionales. Creía esto tan fervientemente que hizo una religión
que rezaba al número 10. “¡Bendícenos, número divino, tú que engendraste dioses y hombres!” él y
sus seguidores rezaban (por qué el “10” era tan especial, no lo sé). Existe la leyenda de que uno de
sus seguidores, Hippasus, demostró que no todos los números son racionales simplemente
demostrando la raíz cuadrada de 2. Esto alteró gravemente el sistema de creencias de Pitágoras, y
respondió ahogando a Hippassus en el mar.
Independientemente, ahora sabemos que no todos los números son racionales.
Numeros reales
Los números reales incluyen tanto números racionales como irracionales. En la práctica, cuando
realiza cualquier trabajo de ciencia de datos, puede tratar cualquier decimal con el que trabaje
como números reales.
Números complejos e imaginarios
Te encuentras con este tipo de número cuando sacas la raíz cuadrada de un número negativo. Si
bien los números imaginarios y complejos tienen relevancia en ciertos tipos de problemas, en su
mayoría nos mantendremos alejados de ellos.
En ciencia de datos, encontrará que la mayoría (si no todo) de su trabajo utilizará números enteros,
números naturales, enteros y números reales. Los números imaginarios se pueden encontrar en
casos de uso más avanzados, como la descomposición de matrices, que abordaremos en el
Capítulo 4.
Orden de operaciones
Con suerte, está familiarizado con el orden de las operaciones, que es el orden en que resuelve
cada parte de una expresión matemática. Como un breve repaso, recuerda que evalúas los
componentes entre paréntesis, seguidos de los exponentes, luego la multiplicación, la división, la
suma y la resta. Puede recordar el orden de las operaciones mediante el recurso mnemotécnico
PEMDAS (Por favor, disculpe a mi querida tía Sally), que corresponde al orden de paréntesis,
exponentes, multiplicación, división, suma y resta.
Tomemos por ejemplo esta expresión:
A continuación resolvemos el exponente, que podemos ver está elevando al cuadrado ese 5 que
acabamos de sumar. Eso es 25:
el , dando :
11
Efectivamente, si tuviéramos que expresar esto en Python imprimiríamos un valor de 6.0 como se
muestra en el Ejemplo 1-1.
Ejemplo 1-1. Resolver una expresión en Python
my_value = 2 * (3 + 2)**2 / 5 - 4
Si bien ambos ejemplos son técnicamente correctos, el último es más claro para nosotros, los
humanos que se confunden fácilmente. Si usted u otra persona realiza cambios en su código, los
paréntesis brindan una referencia fácil del orden de operación a medida que realiza los cambios.
Esto proporciona una línea de defensa contra los cambios de código para evitar errores también.
Variables
Si ha realizado algunos scripts con Python u otro lenguaje de programación, tiene una idea de lo
que es una variable. En matemáticas, una variable es un marcador de posición con nombre para un
número no especificado o desconocido.
Puede tener una variable x que represente cualquier número real y puede multiplicar esa variable
sin declarar cuál es. En el Ejemplo 1-3, tomamos una entrada variable x de un usuario y la
multiplicamos por 3.
Ejemplo 1-3. Una variable en Python que luego se multiplica
x = int(input("Please input a number\n"))
product = 3 * x
print(product)
Hay algunos nombres de variables estándar para ciertos tipos de variables. Si estos nombres de
variables y conceptos no le son familiares, ¡no se preocupe! Pero algunos lectores pueden
reconocer que usamos theta θ para denotar ángulos y beta β para un parámetro en una regresión
lineal. Los símbolos griegos crean nombres de variables incómodos en Python, por lo que
probablemente nombraríamos a estas variables theta y beta en Python como se muestra en el
Ejemplo 1-4.
Ejemplo 1-4. Nombres de variables griegas en Python
beta = 1.75
theta = 30.0
Tenga en cuenta también que los nombres de las variables se pueden subíndice para que se
puedan usar varias instancias de un nombre de variable. Para propósitos prácticos, simplemente
trátelos como variables separadas. Si encuentra variables x 1, x2 y x3, simplemente trátelas como
tres variables separadas como se muestra en el Ejemplo 1-5.
Ejemplo 1-5. Expresando variables con subíndice en Python
x1 = 3 # or x_1 = 3
x2 = 10 # or x_2 = 10
x3 = 44 # or x_3 = 44
Funciones
Las funciones son expresiones que definen relaciones entre dos o más variables. Más
específicamente, una función toma variables de entrada (también llamadas variables de dominio o
variables independientes ), las inserta en una expresión y luego da como resultado una variable de
salida (también llamada variable dependiente ).
Tome esta función lineal simple:
y=2x+1
12
Para cualquier valor de x dado, resolvemos la expresión con esa x para encontrar y. Cuando x = 1,
entonces y = 3. Cuando x = 2, y = 5. Cuando x = 3, y = 7 y así sucesivamente, como se muestra en
la Tabla 1-1.
Diferentes valores para y = 2x +
1
X 2x + 1 y
0 2(0) + 1 1
1 2(1) + 1 3
2 2(2) + 1 5
3 2(3) + 1 7
Las funciones son útiles porque modelan una relación predecible entre variables, como cuántos
incendios podemos esperar a una temperatura x. Usaremos funciones lineales para realizar
regresiones lineales en el Capítulo 5.
Otra convención que puede ver para la variable dependiente y es etiquetarla explícitamente como
una función de x, como f(x). Entonces, en lugar de expresar una función como y = 2 x + 1, también
podemos expresarla como:
f(x)=2x+1
El ejemplo 1-6 muestra cómo podemos declarar una función matemática e iterarla en Python.
Ejemplo 1-6. Declarar una función lineal en Python
def f(x):
return 2 * x + 1
x_values = [0, 1, 2, 3]
for x in x_values:
y = f(x)
print(y)
Cuando se trata de números reales, una característica sutil pero importante de las funciones es que
a menudo tienen un número infinito de valores de x y valores de y resultantes. Pregúntese esto:
¿cuántos valores de x podemos poner a través de la función y = 2 x + 1 ? En lugar de solo 0, 1,
2, 3.. ¿por qué no 0, 0,5, 1, 1,5, 2, 2,5, 3 como se muestra en la Tabla 1-2 ?
Tabla 1-2. Diferentes valores para y = 2x + 1
X 2x + 1 y
0.0 2(0) + 1 1
0.5 2(.5) + 1 2
1.0 2(1) + 1 3
1.5 2(1.5) + 1 4
2.0 2(2) + 1 5
2.5 2(2.5) + 1 6
3.0 2(3) + 1 7
¿O por qué no hacer cuartos de paso para x ? ¿O 1/10 de un paso? Podemos hacer que estos
pasos sean infinitamente pequeños, demostrando efectivamente que y = 2 x + 1 es una función
continua, donde para cada valor posible de x hay un valor para y. Esto nos lleva muy bien a
visualizar nuestra función como una línea, como se muestra en la Figura 1-1.
13
Figura 1-1. Gráfico para la función y = 2x + 1
Cuando graficamos en un plano bidimensional con dos rectas numéricas (una para cada variable)
se conoce como plano cartesiano, plano xy o plano de coordenadas. Trazamos un valor x dado y
luego buscamos el valor y correspondiente, y trazamos las intersecciones como una línea. Tenga
en cuenta que debido a la naturaleza de los números reales (o decimales, si lo prefiere), hay una
cantidad infinita de valores de x. Es por eso que cuando trazamos la función f ( x ) obtenemos una
línea continua sin interrupciones. Hay un número infinito de puntos en esa línea, o cualquier parte
de esa línea.
Si desea trazar esto usando Python, hay varias bibliotecas de gráficos desde Plotly hasta
matplotlib. A lo largo de este libro usaremos SymPy para realizar muchas tareas, y la primera que
usaremos es trazar una función. SymPy usa matplotlib, así que asegúrese de tener ese paquete
instalado. De lo contrario, imprimirá un gráfico feo basado en texto en su consola. Después de eso,
simplemente declare la variable x en SymPy usando símbolos(), declare su función y luego trácela
como se muestra en el Ejemplo 1-7 y la Figura 1-2.
Ejemplo 1-7. Trazar una función lineal en Python usando SymPy
from sympy import *
x = symbols('x')
f = 2*x + 1
plot(f)
14
El ejemplo 1-8 y la figura 1-3 son otro ejemplo que muestra la función f (x) = x2 + 1.
Ejemplo 1-8. Graficar una función exponencial
from sympy import *
x = symbols('x')
f = x**2 + 1
plot(f)
Tenga en cuenta que en la figura 1-3 no obtenemos una línea recta, sino una curva suave y
simétrica conocida como parábola. Es continuo pero no lineal, ya que no produce valores en línea
recta. Funciones con curvas como esta son matemáticamente más difíciles de trabajar, pero
aprenderemos algunos trucos para que no sea tan malo.
FUNCIONES CURVILÍNEAS
Cuando una función es continua pero con curvas, en lugar de lineal y recta, la llamamos función
curvilínea.
Tenga en cuenta que las funciones utilizan múltiples variables de entrada, no solo una. Por
ejemplo, podemos tener una función con variables independientes x e y. Tenga en cuenta que y no
es dependiente como en los ejemplos anteriores.
f( x, y ) = 2x + 3y
Como tenemos dos variables independientes ( x e y ) y una variable dependiente (la salida de f ( x,
y )), necesitamos trazar este gráfico en tres dimensiones para producir un plano de valores en lugar
de una línea, como se muestra en Ejemplo 1-9 y Figura 1-4.
Ejemplo 1-9. Declarar una función con dos variables independientes en Python
from sympy import *
from sympy.plotting import plot3d
x, y = symbols('x y')
f = 2*x + 3*y
plot3d(f)
15
Figura 1-4. Usando SymPy para graficar una función tridimensional
No importa cuántas variables independientes tenga, su función generalmente generará solo una
variable dependiente. Cuando resuelva múltiples variables dependientes, probablemente usará
funciones separadas para cada una.
sumatorias
Prometí no usar ecuaciones llenas de símbolos griegos en este libro. Sin embargo, hay uno que es
tan común y útil que sería negligente no cubrirlo. Una suma se expresa como sigma Σ y suma
elementos.
Por ejemplo, si quiero iterar los números del 1 al 5, multiplicar cada uno por 2 y sumarlos, así es
como lo expresaría usando una suma. El ejemplo 1-10 muestra cómo ejecutar esto en Python.
Tenga en cuenta que i es una variable de marcador de posición que representa cada valor de
índice consecutivo que estamos iterando en el bucle, que multiplicamos por 2 y luego sumamos
todos juntos. Cuando está iterando datos, puede ver variables como x i que indican un elemento
en una colección en el índice i.
LA FUNCIÓN RANGE()
Recuerde que la función range() en Python es exclusiva al final, lo que significa que si invoca
range(1,4) iterará los números 1, 2 y 3. Excluye el 4 como límite superior.
También es común ver que n representa la cantidad de elementos en una colección, como la
cantidad de registros en un conjunto de datos. Aquí hay uno de esos ejemplos donde iteramos una
colección de números de tamaño n, multiplicamos cada uno por 10 y los sumamos:
En el Ejemplo 1-11 usamos Python para ejecutar esta expresión en una colección de cuatro
números. Tenga en cuenta que en Python (y la mayoría de los lenguajes de programación en
general) normalmente hacemos referencia a elementos que comienzan en el índice 0, mientras que
en matemáticas comenzamos en el índice 1. Por lo tanto, cambiamos en consecuencia en nuestra
iteración comenzando en 0 en nuestro RANGE().
Ejemplo 1-11. Suma de elementos en Python
x = [1, 4, 6, 2]
n = len(x)
summation = sum(10*x[i] for i in range(0,n))
print(summation)
Esa es la esencia de la suma. En pocas palabras, una suma Σ dice, "suma un montón de cosas
juntas", y usa un índice i y un valor máximo n para expresar cada iteración que alimenta la suma.
Los veremos a lo largo de este libro.
SUMAS EN SYMPY
Siéntase libre de volver a esta barra lateral más tarde cuando aprenda más sobre SymPy. SymPy,
que usamos para graficar funciones, es en realidad una biblioteca matemática simbólica;
hablaremos de lo que esto significa más adelante en este capítulo. Pero tenga en cuenta para
referencia futura que una operación de suma en SymPy se realiza utilizando el operador Sum(). En
el siguiente código, iteramos i de 1 a n, multiplicamos cada i y los sumamos. Pero luego usamos la
16
función subs() para especificar n como 5, que luego iterará y sumará todos los elementos i del 1 al
n:
from sympy import *
i,n = symbols('i n')
# iterate each element i from 1 to n,
# then multiply and sum
summation = Sum(2*i,(i,1,n))
# specify n as 5,
# iterating the numbers 1 through 5
up_to_5 = summation.subs(n, 5)
print(up_to_5.doit()) # 30
Tenga en cuenta que las sumas en SymPy son perezosas, lo que significa que no se calculan
automáticamente ni se simplifican. Así que usa la función doit() para ejecutar la expresión.
Exponentes
Los exponentes multiplican un número por sí mismo un número específico de veces. Cuando
elevas 2 a la tercera potencia (expresado como 2 3 usando 3 como superíndice), eso es multiplicar
tres 2 juntos:
23 = 2 * 2 * 2 = 8
La base es la variable o valor que estamos exponenciando, y el exponente es el número de veces
que multiplicamos el valor de la base. Para la expresión 2 3 , 2 es la base y 3 es el exponente.
Los exponentes tienen algunas propiedades interesantes. Digamos que multiplicamos x 2 y x 3
juntos. Observe lo que sucede a continuación cuando amplío los exponentes con una simple
multiplicación y luego los consolido en un solo exponente:
x2x3 = (x*x)*(x*x*x) = x2+3 = x5
Cuando multiplicamos exponentes con la misma base, simplemente sumamos los exponentes, lo
que se conoce como la regla del producto. Permítanme enfatizar que la base de todos los
exponentes multiplicados debe ser la misma para que se aplique la regla del producto.
Exploremos la división a continuación. ¿Qué sucede cuando dividimos x 2 por x 5 ?
Como puedes ver, cuando dividimos x2 por x5 podemos cancelar dos x en el numerador y el
denominador, dejándonos con . Cuando existe un factor tanto en el numerador como en el
denominador, podemos cancelar ese factor.
¿Qué pasa con el x-3, te preguntas? Este es un buen punto para introducir exponentes negativos,
que es otra forma de expresar una operación exponencial en el denominador de una fracción. Para
Al vincular la regla del producto, podemos ver que también se aplica a los exponentes negativos.
Para obtener intuición detrás de esto, abordemos este problema de una manera diferente.
Podemos expresar esta división de dos exponentes haciendo negativo el exponente “5” de x 5 y
luego multiplicándolo por x 2. Cuando agrega un número negativo, está realizando efectivamente
17
una resta. Por lo tanto, la regla del producto de exponentes que suma los exponentes multiplicados
aún se mantiene como se muestra a continuación. :
Por último, pero no menos importante, ¿puedes averiguar por qué cualquier base con un exponente
de 0 es 1?
x0 = 1
La mejor manera de obtener esta intuición es razonar que cualquier número dividido por sí mismo
es 1. Si tienes x 3 x 3 es algebraicamente obvio que se reduce a 1. Pero esa expresión
también se evalúa como x 0 :
1 = x 3 x 3 = x 3 x -3 = x 3+-3 = x 0
Por la propiedad transitiva, que establece que si a = b y b = c, entonces a = c, sabemos que x 0
= 1.
Las raíces cúbicas son similares a las raíces cuadradas, pero buscan un número multiplicado por sí
mismo tres veces para dar un resultado. Una raíz cúbica de 8 se expresa como y pregunta
"¿Qué número multiplicado por sí mismo tres veces me da 8?" Este número sería 2 porque 2 * 2 *
2 = 8. En exponentes, una raíz cúbica se expresa como un exponente fraccionario, y se puede
reexpresar como 8 1/3 :
Para volver a cerrar el círculo, ¿qué sucede cuando multiplicas la raíz cúbica de 8 tres veces? Esto
deshará la raíz cúbica y producirá 8. Alternativamente, si expresamos la raíz cúbica como
exponentes fraccionarios , queda claro que sumamos los exponentes para obtener un
exponente de 1. Eso también deshace la raíz cúbica:
Si no está seguro de por qué esto es así, intente expandirlo y verá que la regla de la suma lo
aclara:
18
Por último, ¿qué significa cuando tenemos un exponente fraccionario con un numerador distinto de
1, como ? Bueno, eso es sacar la raíz cúbica de 8 y luego elevarla al cuadrado. Echar un
vistazo:
Y sí, los números irracionales pueden servir como exponentes como 8 π, que es 687,2913. Esto
puede parecer poco intuitivo, ¡y es comprensible! En aras del tiempo, no profundizaremos en esto,
ya que requiere algunos cálculos. Pero esencialmente, podemos calcular exponentes irracionales
aproximando con un número racional. Esto es efectivamente lo que hacen las computadoras, ya
que de todos modos solo pueden calcular hasta tantos lugares decimales.
Por ejemplo, π tiene un número infinito de decimales. Pero si tomamos los primeros 11 dígitos,
3.1415926535, podemos aproximar π como un número racional 31415926535 / 10000000000.
Efectivamente, esto nos da aproximadamente 687.2913, que debería coincidir aproximadamente
con cualquier calculadora.:
logaritmos
Un logaritmo es una función matemática que encuentra una potencia para un número y una base
específicos. Puede que no suene interesante al principio, pero en realidad tiene muchas
aplicaciones. Desde la medición de terremotos hasta la gestión del volumen de su estéreo, el
logaritmo se encuentra en todas partes. También encuentra su camino en el aprendizaje
automático y la ciencia de datos. De hecho, los logaritmos serán una parte clave de las regresiones
logísticas en el Capítulo 6.
Comience su pensamiento preguntando "¿2 elevado a qué potencia me da 8?" Una forma de
expresar esto matemáticamente es usar una x para el exponente:
2x = 8
Intuitivamente sabemos la respuesta, x = 3 , pero necesitamos una forma más elegante de
expresar esta operación matemática común. Para esto es la función log().
Como puede ver en la expresión logarítmica anterior, tenemos una base 2 y estamos encontrando
una potencia que nos dé 8. De manera más general, podemos reexpresar un exponente variable
como un logaritmo:
ax =b
Hablando algebraicamente, esta es una forma de aislar x, lo cual es importante para resolver para
x. El ejemplo 1-12 muestra cómo calculamos este logaritmo en Python.
Ejemplo 1-12. Usando la función de registro en Python
from math import log
# 2 raised to what power gives me 8?
x = log(8, 2)
print(x) # prints 3.0
Cuando no proporciona un argumento base a una función log() en una plataforma como Python,
normalmente tendrá una base predeterminada. En algunos campos, como las mediciones de
terremotos, la base predeterminada para el registro es 10. Pero en la ciencia de datos, la base
predeterminada para el registro es el número e de Euler. Python usa este último, y hablaremos de
e en breve.
Al igual que los exponentes, los logaritmos tienen varias propiedades cuando se trata de
multiplicaciones, divisiones, exponenciaciones, etc. En aras del tiempo y el enfoque, solo
presentaré esto en la Tabla 1-3. La idea clave en la que centrarse es que un logaritmo encuentra
un exponente para una base dada para dar como resultado un número determinado.
Si necesita sumergirse en las propiedades logarítmicas, la Tabla 1-3 muestra los comportamientos
de exponentes y logaritmos uno al lado del otro que puede usar como referencia.
19
Tabla 1-3. Propiedades para exponentes y logaritmos
Reuniendo los conceptos de exponentes que aprendimos hasta ahora (o tal vez sacando un libro
de texto de finanzas), podemos encontrar una fórmula para calcular el interés. Consiste en un saldo
A para una inversión inicial P, tasa de interés r, lapso de tiempo t (número de años) y períodos n
(número de meses en cada año). Aquí está la fórmula:
Entonces, si fuéramos a capitalizar el interés cada mes, el préstamo crecería a $148.69 como se
calcula aquí:
20
Si quiere hacer esto en Python, pruébelo con el código del Ejemplo 1-13.
Ejemplo 1-13. Cálculo de interés compuesto en Python
from math import exp
p = 100
r = .20
t = 2.0
n = 12
a = p * (1 + (r/n))**(n * t)
print(a) # prints 148.69146179463576
Pero, ¿y si capitalizáramos el interés diariamente? ¿Qué pasa entonces? Cambie n a 365:
Bien, solo estamos ganando fracciones cada vez más pequeñas de un centavo cuanto más
frecuentemente calculamos. Entonces, si sigo haciendo estos períodos infinitamente más pequeños
hasta el punto de capitalizarlos continuamente, ¿adónde lleva esto?
Permítame presentarle el número e de Euler, que es aproximadamente 2,71828. Aquí está la
fórmula para capitalizar "continuamente", lo que significa que estamos capitalizando sin parar:
Volviendo a nuestro ejemplo, calculemos el saldo de nuestro préstamo después de dos años si
capitalizamos continuamente:
Esto no es demasiado sorprendente considerando que la capitalización de cada minuto nos dio un
saldo de 149.1824584. Eso nos acercó mucho a nuestro valor de 149.1824698 al capitalizar
continuamente.
21
Por lo general, usa e como base de exponente en Python, Excel y otras plataformas que usan la
función exp(). Descubrirá que e se usa con tanta frecuencia que es la base predeterminada para
las funciones de exponente y logaritmo.
El ejemplo 1-14 calcula el interés continuo en Python usando lafunción exp().
Ejemplo 1-14. Cálculo de interés continuo en Python
from math import exp
p = 100 # principal, starting amount
r = .20 # interest rate, by year
t = 2.0 # time, number of years
a = p * exp(r*t)
print(a) # prints 149.18246976412703
Entonces, ¿de dónde derivamos esta constante e? Compara la fórmula de interés compuesto y la
fórmula de interés continuo. Estructuralmente se ven similares pero tienen algunas diferencias:
Logaritmos naturales
Cuando usamos e como nuestra base para un logaritmo, lo llamamos logaritmo natural.
Dependiendo de la plataforma, podemos usar ln() en lugar de log() para especificar un logaritmo
natural. Entonces, en lugar de expresar un logaritmo natural expresado como log e10 para
encontrar la potencia elevada en e para obtener 10, lo abreviaríamos como ln(10):
loge10 = ln (10)
Sin embargo, en Python, la función log() especifica un logaritmo natural. Como se mencionó
anteriormente, la base predeterminada para la función log() es e. Simplemente deje vacío el
segundo argumento para la base y usará de forma predeterminada e como la base que se
muestra en el Ejemplo 1-15.
22
Ejemplo 1-15. Cálculo del logaritmo natural de 10 en Python
from math import log
# e raised to what power gives us 10?
x = log(10)
print(x) # prints 2.302585092994046
Usaremos e en varios lugares a lo largo de este libro. Siéntase libre de experimentar con
exponentes y logaritmos utilizando Excel, Python, Desmos.com o cualquier otra plataforma de
cálculo de su elección. Haz gráficos y siéntete cómodo con el aspecto de estas funciones.
Límites
Como hemos visto con el número de Euler, surgen algunas ideas interesantes cuando siempre
aumentamos o disminuimos una variable de entrada y la variable de salida se acerca a un valor
pero nunca lo alcanza. Exploremos formalmente esta idea.
Tome esta función, que se representa en la figura 1-5 :
Figura 1-5. Una función que siempre tiende a 0 pero nunca llega a 0
Estamos buscando solo valores positivos de x. Tenga en cuenta que a medida que x aumenta para
siempre, f(x) se acerca a 0. Curiosamente, f(x) nunca llega a 0. Simplemente se acerca cada vez
más.
Por lo tanto, el destino de esta función es que, como x siempre se extiende hasta el infinito,
seguirá acercándose a 0 pero nunca llegará a 0. La forma en que expresamos un valor que
siempre se aproxima, pero nunca se alcanza, es a través de un límite:
La forma en que leemos esto es "cuando x tiende a infinito, la función 1/x tiende a 0 (pero nunca
llega a 0)". Verá mucho este tipo de comportamiento de "acercarse pero nunca tocarse",
especialmente cuando nos sumergimos en derivadas e integrales.
Usando SymPy, podemos calcular a qué valor nos acercamos para cuando x tiende a
infinito ∞ ( Ejemplo 1-16 ). Tenga en cuenta que ∞ se expresa hábilmente en SymPy con oo.
Ejemplo 1-16. Usando SymPy para calcular límites
from sympy import *
x = symbols('x')
f=1/x
result = limit(f, x, oo)
print(result) # 0
23
Como has visto, también descubrimos el número e de Euler de esta manera. Es el resultado de
extender n para siempre hasta el infinito para esta función:
Curiosamente, cuando calculamos el número de Euler con límites en SymPy (que se muestra en el
siguiente código), SymPy lo reconoce inmediatamente como el número de Euler. Podemos llamar a
evalf() para que podamos mostrarlo como un número:
from sympy import *
n = symbols('n')
f = (1 + (1/n))**n
result = limit(f, n, oo)
print(result) # E
print(result.evalf()) # 2.71828182845905
EL PODER DE SYMPY
SymPy es un poderoso y fantástico sistema de álgebra computarizada (CAS) para Python que usa el
cálculo simbólico exacto en lugar del cálculo aproximado usando decimales. Es útil para aquellas
situaciones en las que usaría "lápiz y papel" para resolver problemas de matemáticas y cálculo, con
el beneficio de usar la sintaxis familiar de Python. En lugar de representar la raíz cuadrada de 2
aproximando 1,4142135623730951, la conservará exactamente como s qrt (2).
Entonces, ¿por qué no usar SymPy para todo lo relacionado con las matemáticas? Si bien lo
usaremos a lo largo de este libro, es importante seguir sintiéndose cómodo haciendo matemáticas de
Python con decimales simples, ya que scikit-learn y otras bibliotecas de ciencia de datos adoptan
este enfoque. Es mucho más rápido para las computadoras usar decimales en lugar de símbolos.
SymPy también tiene problemas cuando las expresiones matemáticas comienzan a hacerse
demasiado grandes. Pero mantenga SymPy en su bolsillo trasero como su ventaja, y no se lo cuente
a sus hijos de secundaria y universitarios. Literalmente pueden usarlo para hacer su tarea de
matemáticas.
Derivados
Volvamos a hablar de funciones y mirémoslas desde una perspectiva de cálculo, comenzando con
las derivadas. Una derivada indica la pendiente de una función y es útil para medir la tasa de
cambio en cualquier punto de una función.
¿Por qué nos preocupamos por los derivados? A menudo se utilizan en el aprendizaje automático y
otros algoritmos matemáticos, especialmente con descenso de gradiente. Cuando la pendiente es
0, eso significa que estamos en el mínimo o máximo de una variable de salida. Este concepto será
útil más adelante cuando hagamos regresión lineal ( Capítulo 5 ), regresión logística ( Capítulo 6 ) y
redes neuronales ( Capítulo 7 ).
Comencemos con un ejemplo simple. Echemos un vistazo a la función en la Figura
1-6. ¿Qué tan “empinada” es la curva en x = 2 ?
Observe que podemos medir la "inclinación" en cualquier punto de la curva, y podemos visualizar
esto con una línea tangente. Piense en una línea tangente como una línea recta que "apenas toca"
la curva en un punto dado. También proporciona la pendiente en un punto dado. Puede estimar
crudamente una línea tangente en un valor x dado al crear una línea que interseque ese valor x y
un valor x vecino muy cercano en la función.
Tome x = 2 y un valor cercano x = 2.1, que cuando se pasa a la función f(x) = x 2 producirá f (2) = 4
y f (2.1) = 4.41 como se muestra en la Figura 1-7. La recta resultante que pasa por estos dos
puntos tiene una pendiente de 4,1.
24
Figura 1-6. Observando la pendiente en una parte dada de la función
Puede calcular rápidamente la pendiente m entre dos puntos usando la fórmula simple de
elevación sobre carrera:
m = 41
Si hiciera el paso x entre los dos puntos aún más pequeño, como x = 2 y x = 2,00001, lo que daría
como resultado f (2) = 4 y f (2,00001) = 4,00004, eso se acercaría mucho a la pendiente real de 4.
Entonces, cuanto menor sea el paso al valor vecino, más nos acercaremos al valor de la pendiente
en un punto dado de la curva. Como tantos conceptos importantes en matemáticas, encontramos
algo significativo cuando nos acercamos a valores infinitamente grandes o infinitamente pequeños.
El ejemplo 1-17 muestra una calculadora derivada implementada en Python.
Ejemplo 1-17. Una calculadora de derivadas en Python
def derivative_x(f, x, step_size):
m = (f(x + step_size) - f(x)) / ((x + step_size) - x)
return m
def my_function(x):
return x**2
slope_at_2 = derivative_x(my_function, 2, .00001)
print(slope_at_2) # prints 4.000010000000827
25
Ahora, la buena noticia es que hay una forma más limpia de calcular la pendiente en cualquier
parte de una función. Ya hemos estado usando SymPy para trazar gráficos, pero le mostraré cómo
también puede realizar tareas como derivadas utilizando la magia de la computación simbólica.
Cuando encuentre una función exponencial como f(x)= x2, la función derivada hará que el
exponente sea un multiplicador y luego lo reducirá en 1, dejándonos con la derivada . El
indica una derivada con respecto a x, lo que dice que estamos construyendo una derivada
apuntando al valor de x para obtener su pendiente. Entonces, si queremos encontrar la pendiente
en x = 2, y tenemos la función derivada, simplemente reemplazamos ese valor de x para obtener la
pendiente:
f(x) = x2
Si tiene la intención de aprender estas reglas para calcular derivadas a mano, hay muchos libros de
cálculo para eso. Pero hay algunas buenas herramientas para calcular derivadas simbólicamente
para ti. La biblioteca de Python SymPy es gratuita y de código abierto, y se adapta muy bien al uso
de la sintaxis de Python. El ejemplo 1-18 muestra cómo calcular la derivada de f(x) = x 2 en
SymPy.
Ejemplo 1-18. Cálculo de una derivada en SymPy
from sympy import *
# Declare 'x' to SymPy
x = symbols('x')
# Now just use Python syntax to declare function
f = x**2
# Calculate the derivative of the function
dx_f = diff(f)
print(dx_f) # prints 2*x¡
Guau! Entonces, al declarar variables usando la función de símbolos () en SymPy, puedo proceder
a usar la sintaxis normal de Python para declarar mi función. Después de eso, puedo usar diff()
para calcular la función derivada. En el ejemplo 1-19, podemos llevar nuestra función derivada de
vuelta a Python simple y simplemente declararla como otra función.
Ejemplo 1-19. Una calculadora de derivadas en Python
def f(x):
return x**2
def dx_f(x):
return 2*x
slope_at_2 = dx_f(2.0)
print(slope_at_2) # prints 4.0
Si quieres seguir usando SymPy, puede llamar a la función subs() para intercambiar la variable x
con el valor 2 como se muestra en el ejemplo 1-20.
Ejemplo 1-20. Uso de la función de sustitución en SymPy
# Calculate the slope at x = 2
print(dx_f.subs(x,2)) # prints 4
Derivadas parciales
Otro concepto que encontraremos en este libro es el de derivadas parciales, que usaremos en los
capítulos 5, 6 y 7. Estas son derivadas de funciones que tienen múltiples variables de entrada.
Piénsalo de esta manera. En lugar de encontrar la pendiente en una función unidimensional,
tenemos pendientes con respecto a múltiples variables en varias direcciones. Para cada derivada
variable dada, asumimos que las otras variables se mantienen constantes. Mire el gráfico 3D de
26
f( x, y) = 2x3 + 3y3 en la figura 1-8 y verá que tenemos pendientes en dos direcciones para dos
variables.
Tomemos la función f(x, y)= 2x3 + 3y3 . Las variables x e y obtienen cada una sus propias
derivadas . Estos representan los valores de pendiente con respecto a cada variable
en una superficie multidimensional. Técnicamente llamamos a estos gradientes de "pendientes"
cuando se trata de múltiples dimensiones. Estas son las derivadas de x e y, seguidas del código
SymPy para calcular esas derivadas.:
El ejemplo 1-21 y la figura 1-8 muestran cómo calculamos las derivadas parciales para x e y,
respectivamente, con SymPy.
Ejemplo 1-21. Cálculo de derivadas parciales con SymPy
from sympy import *
from sympy.plotting import plot3d
# Declare x and y to SymPy
x,y = symbols('x y')
# Now just use Python syntax to declare function
f = 2*x**3 + 3*y**3
# Calculate the partial derivatives for x and y
dx_f = diff(f, x)
dy_f = diff(f, y)
print(dx_f) # prints 6*x**2
print(dy_f) # prints 9*y**2
# plot the function
plot3d(f)
Así que para ( x, y ) valores (1,2), la pendiente con respecto a x es 6(1)=6 y la pendiente con
respecto a y es 9 (2) 2= 36 .
27
USO DE LÍMITES PARA CALCULAR DERIVADAS
¿Quiere ver dónde entran en juego los límites al calcular derivadas? Si te sientes bien con lo que
hemos aprendido hasta ahora, ¡continúa! Si todavía está digiriendo, tal vez considere volver a esta
barra lateral más tarde.
SymPy nos permite hacer algunas exploraciones interesantes sobre matemáticas. Tome nuestra
función f (x ) = x2; aproximamos una pendiente para x = 2 dibujando una línea a través de un
punto vecino cercano x = 2.0001 agregando un paso 0.0001. ¿Por qué no usar un límite para
disminuir para siempre ese paso s y ver a qué pendiente se acerca?
En nuestro ejemplo, estamos interesados en la pendiente donde x=2 , así que sustituyamos
eso:
Al acercarnos siempre a un tamaño de paso s a 0 pero nunca alcanzarlo (recuerde que el punto
vecino no puede tocar el punto en x = 2 , de lo contrario no tenemos línea), podemos usar un
límite para ver que convergemos en una pendiente de 4 como se muestra en el ejemplo 1-22.
Ejemplo 1-22. Uso de límites para calcular una pendiente
from sympy import *
# "x" and step size "s"
x, s = symbols('x s')
# declare function
f = x**2
# slope between two points with gap "s"
# substitute into rise-over-run formula
slope_f = (f.subs(x, x + s) - f) / ((x+s) - x)
# substitute 2 for x
slope_2 = slope_f.subs(x, 2)
# calculate slope at x = 2
# infinitely approach step size _s_ to 0
result = limit(slope_2, s, 0)
print(result) # 4
Ahora, ¿qué pasa si no asignamos un valor específico a x y lo dejamos en paz? ¿Qué sucede si
disminuimos nuestro tamaño de paso s infinitamente hacia 0? Veamos el ejemplo 1-23.
Ejemplo 1-23. Uso de límites para calcular una derivada
from sympy import *
# "x" and step size "s"
x, s = symbols('x s')
# declare function
f = x**2
# slope between two points with gap "s"
# substitute into rise-over-run formula
slope_f = (f.subs(x, x + s) - f) / ((x+s) - x)
# calculate derivative function
# infinitely approach step size +s+ to 0
result = limit(slope_f, s, 0)
print(result) # 2x
Eso nos dio nuestra función derivada 2x. SymPy fue lo suficientemente inteligente como para darse
cuenta de que nunca dejaría que nuestro tamaño de paso llegara a 0 sino que siempre se acercara
a 0. Esto converge f( x ) = x2 para llegar a su contraparte derivada 2x.
28
La regla de la cadena
The Chain Rule En el Capítulo 7, cuando construyamos una red neuronal, vamos a necesitar un
truco matemático especial llamado regla de la cadena. Cuando compongamos las capas de la red
neuronal, tendremos que desenredar las derivadas de cada capa. Pero por ahora aprendamos la
regla de la cadena con un ejemplo algebraico simple. Digamos que te dan dos funciones:
y = x2 + 1
z = y3 - 2
Observe que estas dos funciones están vinculadas, porque y es la variable de salida en la primera
función pero es la variable de entrada en la segunda. Esto significa que podemos sustituir la
primera función y en la segunda función z así:
Pero mira esto. Comencemos de nuevo y tomemos un enfoque diferente. Si tomamos las derivadas
de las funciones y y z por separado, y luego las multiplicamos juntas, ¡esto también produce la
derivada de z con respecto a x ! Vamos a intentarlo:
Muy bien, 6 xy2 puede no parecerse a 6 x (x 2 +1) 2, pero eso es solo porque aún no hemos
sustituido la función y. Haga eso para que toda la derivada se exprese en términos de x sin
y.
29
El ejemplo 1-25 muestra el código SymPy que hace esta comparación, mostrando que la derivada
de la regla de la cadena es igual a la derivada de la función sustituida.
Ejemplo 1-25. Calcular la derivada dz/dx con y sin la regla de la cadena, pero aún obteniendo la
misma respuesta
from sympy import *
x, y = symbols('x y')
# derivative for first function
# need to underscore y to prevent variable clash
_y = x**2 + 1
dy_dx = diff(_y)
# derivative for second function
z = y**3 - 2
dz_dy = diff(z)
# Calculate derivative with and without
# chain rule, substitute y function
dz_dx_chain = (dy_dx * dz_dy).subs(y, _y)
dz_dx_no_chain = diff(z.subs(y, _y))
# Prove chain rule by showing both are equal
print(dz_dx_chain) # 6*x*(x**2 + 1)**2
print(dz_dx_no_chain) # 6*x*(x**2 + 1)**2
La regla de la cadena es una parte clave del entrenamiento de una red neuronal con los pesos y
sesgos adecuados. En lugar de desenredar la derivada de cada nodo en forma de cebolla anidada,
podemos multiplicar las derivadas en cada nodo, lo cual es matemáticamente mucho más fácil.
Integrales
Lo contrario de una derivada es una integral, que encuentra el área bajo la curva para un rango
dado. En los Capítulos 2 y 3, encontraremos las áreas bajo distribuciones de probabilidad. Aunque
no usaremos integrales directamente, y en su lugar usaremos funciones de densidad acumulativa
que ya están integradas, es bueno saber cómo las integrales encuentran áreas bajo curvas. El
Apéndice A contiene ejemplos del uso de este enfoque en distribuciones de probabilidad.
Quiero adoptar un enfoque intuitivo para aprender integrales llamado Reimann Sums, uno que se
adapte de manera flexible a cualquier función continua. Primero, señalemos que encontrar el área
de un rango debajo de una línea recta es fácil. Digamos que tengo una función f( x ) = 2x y quiero
encontrar el área debajo de la línea entre 0 y 1, como se muestra en la figura 1-9.
30
Figura 1-9. Cálculo de un área bajo una función lineal
Observe que estoy encontrando el área delimitada entre la línea y el eje x, y en el rango x de 0,0 a
1,0. Si recuerdas las fórmulas básicas de geometría, el área A de un triángulo es donde b
es la longitud de la base y h es la altura. Podemos detectar visualmente que b = 1 y h = 2.
Entonces, al conectarnos a la fórmula, obtenemos para nuestra área 1.0 como se calcula aquí:
A=1
Eso no estuvo mal, ¿verdad? Pero veamos una función en la que es difícil encontrar el área debajo:
f ( x ) = x 2+ 1. ¿Cuál es el área entre 0 y 1 sombreada en la figura 1-10 ?
31
¿Qué pasa si empaquetamos cinco rectángulos de igual longitud debajo de la curva como se
muestra en la Figura 1-11, donde la altura de cada uno se extiende desde el eje x hasta donde el
punto medio toca la curva?
El área de un rectángulo es A = length × width A = largo × ancho , por lo que podríamos sumar
fácilmente las áreas de los rectángulos. ¿Nos daría eso una buena aproximación del área bajo la
curva? ¿Qué pasa si empacamos 100 rectángulos? 1,000? 100,000? A medida que aumentamos el
número de rectángulos mientras disminuimos su ancho, ¿no nos acercaríamos al área bajo la
curva? Sí, lo haríamos, y es otro caso más en el que aumentamos/disminuimos algo hacia el infinito
para acercarnos a un valor real.
Figura 1-11. Rectángulos de embalaje debajo de una curva para aproximar el área
Probémoslo en Python. Primero necesitamos una función que aproxime una integral que
llamaremos approximate_integral(). Los argumentos a y b especificarán el mínimo y el máximo del
rango x, respectivamente. n será el número de rectángulos a empaquetar y f será la función que
estamos integrando. Implementamos la función en el Ejemplo 1-26 y luego la usamos para integrar
nuestra función f ( x ) = x2+ 1 con cinco rectángulos, entre 0.0 y 1.0.
Ejemplo 1-26. Una aproximación integral en Python
def approximate_integral(a, b, n, f):
delta_x = (b - a) / n
total_sum = 0
for i in range(1, n + 1):
midpoint = 0.5 * (2 * a + delta_x * (2 * i - 1))
total_sum += f(midpoint)
return total_sum * delta_x
def my_function(x):
return x**2 + 1
area = approximate_integral(a=0, b=1, n=5, f=my_function)
print(area) # prints 1.33
Entonces obtenemos un área de 1.33. ¿Qué pasa si usamos 1,000 rectángulos? Probémoslo en el
ejemplo 1-27.
Ejemplo 1-27. Otra aproximación integral en Python
area = approximate_integral(a=0, b=1, n=1000, f=my_function)
print(area) # prints 1.333333250000001
Bien, estamos obteniendo algo más de precisión aquí y obteniendo algunos lugares
decimales más. ¿Qué pasa con un millón de rectángulos como se muestra en el Ejemplo
1-28 ?
32
Ejemplo 1-28. Otra aproximación integral en Python
area = approximate_integral(a=0, b=1, n=1_000_000, f=my_function)
print(area) # prints 1.3333333333332733
Bien, creo que estamos obteniendo un rendimiento decreciente aquí y convergiendo en el
valor 1. 333 ¯ donde la parte ".333" se repite para siempre. Si fuera un número racional,
probablemente 4/3 = 1. 333. A medida que aumentamos el número de rectángulos, la
aproximación comienza a alcanzar su límite con decimales cada vez más pequeños.
Ahora que tenemos cierta intuición sobre lo que estamos tratando de lograr y por qué,
hagamos un enfoque más exacto con SymPy, que es compatible con los números
racionales, en el Ejemplo 1-29.
Ejemplo 1-29. Uso de SymPy para realizar la integración
from sympy import *
# Declare 'x' to SymPy
x = symbols('x')
# Now just use Python syntax to declare function
f = x**2 + 1
# Calculate the integral of the function with respect to x
# for the area between x = 0 and 1
area = integrate(f, (x, 0, 1))
print(area) # prints 4/3¡
Enfriar! Entonces, el área en realidad es 4/3, que es en lo que convergió nuestro método
anterior. Desafortunadamente, Python simple (y muchos lenguajes de programación) solo
admiten decimales, pero los sistemas de álgebra computacional como SymPy nos brindan
números racionales exactos. Usaremos integrales para encontrar áreas bajo curvas en los
Capítulos 2 y 3, aunque scikit-learn hará el trabajo por nosotros.
33
# of rectangles "n" to infinity
area = limit(n_rectangles, n, oo)
print(area) # prints 4/3
Aquí determinamos la longitud de cada rectángulo delta_x y el inicio de cada rectángulo x_i
donde i es el índice de cada rectángulo. fx_i es la altura de cada rectángulo en el índice i.
Declaramos un número n de rectángulos y sumamos sus áreas delta_x * fx_i, pero aún no
tenemos un valor de área porque no hemos asignado un número para n. En cambio, nos
acercamos a n hacia el infinito para ver en qué área convergemos, ¡y deberías obtener 4/3!
Conclusión
En este capítulo cubrimos algunos fundamentos que usaremos para el resto de este libro.
Desde la teoría de números hasta los logaritmos y las integrales de cálculo, destacamos
algunos conceptos matemáticos importantes relevantes para la ciencia de datos, el
aprendizaje automático y el análisis. Es posible que tenga preguntas acerca de por qué
estos conceptos son útiles. ¡Eso vendrá después!
Antes de pasar a discutir la probabilidad, tómate un poco de tiempo para repasar estos
conceptos una vez más y luego haz los siguientes ejercicios. Siempre puede volver a
visitar este capítulo a medida que avanza en este libro y actualizar según sea necesario
cuando comience a aplicar estas ideas matemáticas.
Ejercicios
1. ¿El valor 62.6738 es racional o irracional? ¿Por qué o por qué no?
2. Evalúa la expresión: 10710-5
3. Evalúa la expresión:
4. Evalúa la expresión:
5. Suponiendo que no se hagan pagos, ¿cuánto valdría un préstamo de $1,000
al 5% de interés compuesto mensualmente después de 3 años?
6. Suponiendo que no se hagan pagos, ¿cuánto valdría un préstamo de $1,000
al 5% de interés compuesto continuamente después de 3 años?
7. Para la función f( x ) = 3 x2 + 1 ¿cuál es la pendiente en x = 3?
8. Para la función f ( x ) = 3 x 2 + 1 ¿cuál es el área bajo la curva de x entre 0 y
2?
Las respuestas se encuentran en el Apéndice B.
34
Capítulo 2. Probabilidad
Cuando piensas en probabilidad, ¿qué imágenes te vienen a la mente? Tal vez piense en ejemplos
relacionados con los juegos de azar, como la probabilidad de ganar la lotería o conseguir un par
con dos dados. Tal vez esté prediciendo el rendimiento de las acciones, el resultado de una
elección política o si su vuelo llegará a tiempo. Nuestro mundo está lleno de incertidumbres que
queremos medir.
Quizá esa sea la palabra en la que deberíamos centrarnos: incertidumbre. ¿Cómo medimos algo
de lo que no estamos seguros?
Al final, la probabilidad es el estudio teórico de medir la certeza de que ocurrirá un evento. Es una
disciplina fundamental para las estadísticas, las pruebas de hipótesis, el aprendizaje automático y
otros temas de este libro. Mucha gente da por sentada la probabilidad y asume que la entiende. Sin
embargo, es más matizado y complicado de lo que la mayoría de la gente piensa. Si bien los
teoremas y las ideas de probabilidad son matemáticamente sólidos, se vuelve más complejo
cuando introducimos datos y nos aventuramos en las estadísticas. Cubriremos eso en el Capítulo 4
sobre estadística y prueba de hipótesis.
En este capítulo, discutiremos qué es la probabilidad. Luego cubriremos los conceptos matemáticos
de probabilidad, el teorema de Bayes, la distribución binomial y la distribución beta.
comprensión de la probabilidad
La probabilidad es la fuerza con la que creemos que ocurrirá un evento, a menudo expresada como
un porcentaje. Aquí hay algunas preguntas que podrían garantizar una probabilidad de respuesta:
¿Qué probabilidades hay de que salgan 7 caras en 10 lanzamientos de moneda justos?
¿Cuáles son mis posibilidades de ganar una elección?
¿Se retrasará mi vuelo?
¿Qué tan seguro estoy de que un producto es defectuoso?
La forma más popular de expresar la probabilidad es como un porcentaje, como en "Hay un 70% de
posibilidades de que mi vuelo llegue tarde". Llamaremos a esta probabilidad P( X ), donde X es el
evento de interés. Sin embargo, a medida que trabaja con probabilidades, es más probable que lo
vea expresado como un decimal (en este caso, 0,70), que debe estar entre 0,0 y 1,0:
P ( X ) =.70
La probabilidad es similar a la probabilidad, y es fácil confundirlos (muchos diccionarios también lo
hacen). Puede salirse con la suya usando "probabilidad" y "verosimilitud" indistintamente en una
conversación cotidiana. Sin embargo, debemos precisar estas diferencias. La probabilidad se trata
de cuantificar las predicciones de eventos que aún no han sucedido, mientras que la probabilidad
mide la frecuencia de los eventos que ya ocurrieron. En estadísticas y aprendizaje automático, a
menudo usamos la probabilidad (el pasado) en forma de datos para predecir la probabilidad (el
futuro).
Es importante tener en cuenta que la probabilidad de que ocurra un evento debe estar
estrictamente entre 0% y 100%, o 0.0 y 1.0. Lógicamente, esto significa que la probabilidad de que
un evento no suceda se calcula restando la probabilidad del evento de 1.0:
P ( X ) =.70
P ( not X ) = 1 -.70 =.30
Esta es otra distinción entre probabilidad y verosimilitud. Las probabilidades de todos los posibles
resultados mutuamente excluyentes para un evento (lo que significa que solo puede ocurrir un
resultado, no múltiples) deben sumar 1.0 o 100%. Las probabilidades, sin embargo, no están
sujetas a esta regla.
Alternativamente, la probabilidad se puede expresar como una probabilidad O ( X ) como 7:3, 7/3
o 2. 333 ¯ .
Para convertir una cuota O ( X ) en una probabilidad proporcional P( X ) , utilice esta fórmula:
Entonces, si tengo una probabilidad de 7/3, puedo convertirla en una probabilidad proporcional
como esta:
35
Por el contrario, puede convertir una cuota en una probabilidad simplemente dividiendo la
probabilidad de que ocurra el evento por la probabilidad de que no ocurra:
matemáticas de probabilidad
Cuando trabajamos con una sola probabilidad de un evento P ( X ), conocida como probabilidad
marginal, la idea es bastante sencilla, como se discutió anteriormente. Pero cuando comenzamos a
combinar probabilidades de diferentes eventos, se vuelve un poco menos intuitivo.
Probabilidades conjuntas
Digamos que tienes una moneda justa y un dado de seis caras justo. Quiere encontrar la
probabilidad de sacar cara y sacar un 6 en la moneda y el dado, respectivamente. Estas son dos
probabilidades separadas de dos eventos separados, pero queremos encontrar la probabilidad de
que ambos eventos ocurran juntos. Esto se conoce como probabilidad conjunta.
36
Piense en una probabilidad conjunta como un operador AND. Quiero encontrar la probabilidad de
sacar cara Y sacar un 6. Queremos que ambos eventos sucedan juntos, entonces, ¿cómo
calculamos esta probabilidad?
Hay dos lados en una moneda y seis lados en el dado, por lo que la probabilidad de cara es 1/2 y la
probabilidad de seis es 1/6. La probabilidad de que ocurran ambos eventos (suponiendo que sean
independientes, ¡más sobre esto más adelante!) es simplemente multiplicar los dos juntos:
P(A AND B) = P(A) × P(B)
Bastante fácil, pero ¿por qué es este el caso? Se pueden descubrir muchas reglas de probabilidad
generando todas las combinaciones posibles de eventos, lo que proviene de un área de
matemáticas discretas conocida como permutaciones y combinaciones. Para este caso, genere
todos los resultados posibles entre la moneda y el dado, emparejando cara (H) y cruz (T) con los
números del 1 al 6. Tenga en cuenta que puse asteriscos "*" alrededor del resultado de interés
donde obtenemos cara y un 6:
H1 H2 H3 H4 H5 *H6* T1 T2 T3 T4 T5 T6
Observe que hay 12 resultados posibles al lanzar nuestra moneda y lanzar nuestro dado. El único
que nos interesa es "H6", obtener cara y 6. Entonces, debido a que solo hay un resultado que
satisface nuestra condición, y hay 12 resultados posibles, la probabilidad de obtener cara y 6 es
1/12.
En lugar de generar todas las combinaciones posibles y contar las que nos interesen, nuevamente
podemos usar la multiplicación como un atajo para encontrar la probabilidad conjunta. Esto se
conoce como la regla del producto :
P(A AND B) = P(A) × P(B)
Unión de probabilidades
Discutimos las probabilidades conjuntas, que es la probabilidad de que dos o más eventos ocurran
simultáneamente. Pero, ¿qué pasa con la probabilidad de obtener el evento A o B?Cuando
tratamos con operaciones OR con probabilidades, esto se conoce como probabilidad de unión.
Comencemos con eventos mutuamente excluyentes, que son eventos que no pueden ocurrir
simultáneamente. Por ejemplo, si tiro un dado, no puedo obtener simultáneamente un 4 y un 6.
Solo puedo obtener un resultado. Obtener la probabilidad de unión para estos casos es fácil.
Simplemente los sumo. Si quiero encontrar la probabilidad de obtener un 4 o un 6 en una tirada de
dado, será 2/6 = 1/3:
Pero, ¿qué pasa con los eventos no mutuamente excluyentes, que son eventos que pueden ocurrir
simultáneamente? Volvamos al ejemplo del lanzamiento de la moneda y la tirada del dado. ¿Cuál
37
es la probabilidad de obtener cara O un 6? Antes de que tenga la tentación de agregar esas
probabilidades, generemos todos los resultados posibles nuevamente y resaltemos los que nos
interesan.:
*H1* *H2* *H3* *H4* *H5* *H6* T1 T2 T3 T4 T5 *T6*
Aquí estamos interesados en todos los resultados de cabeza, así como en los 6 resultados. Si
proporcionamos los 7 de los 12 resultados que nos interesan, 7/12, obtenemos una probabilidad
correcta de .58 333.
Pero, ¿qué sucede si sumamos las probabilidades de cara y 6 juntas? Obtenemos una respuesta
diferente (¡y equivocada!) de . 666:
Tenga en cuenta que esta fórmula también se aplica a eventos mutuamente excluyentes. Si los
eventos son mutuamente excluyentes donde solo se permite un resultado A o B pero no ambos,
38
entonces la probabilidad conjunta P ( A Y B ) será 0 y, por lo tanto, se eliminará de la fórmula.
Entonces te queda simplemente sumar los eventos como lo hicimos antes.
En resumen, cuando tiene una probabilidad de unión entre dos o más eventos que no son
mutuamente excluyentes, asegúrese de restar la probabilidad conjunta para que no se cuenten dos
veces las probabilidades.
Probabilidad Condicional y Teorema de Bayes
Un tema de probabilidad que fácilmente confunde a las personas es el concepto de probabilidad
condicional, que es la probabilidad de que ocurra un evento A dado que ha ocurrido un evento B.
Normalmente se expresa como P (A DADO B) o P (A|B).
Digamos que un estudio afirma que el 85% de los pacientes con cáncer beben café. ¿Cómo
reacciona ante esta afirmación? ¿Esto te alarma y te da ganas de abandonar tu bebida matutina
favorita? Primero definamos esto como una probabilidad condicional P(Coffee given Cancer) or
P(Coffee|Cancer) P ( Café dado Cáncer ) o P ( Café | Cáncer ). Esto representa una probabilidad
de que las personas tomen café dado que tienen cáncer.
Dentro de los Estados Unidos, comparemos esto con el porcentaje de personas diagnosticadas con
cáncer (0.5% según cancer.gov ) y el porcentaje de personas que beben café (65% según
statista.com ):
P(Coffee) = .65
P(Cancer) = .005
P(Coffee|Cancer) = .85
Hmmmm.. estudie estos números por un momento y pregúntese si el café es realmente el
problema aquí. Observe nuevamente que solo el 0.5% de la población tiene cáncer en un momento
dado. Sin embargo, el 65% de la población bebe café regularmente. Si el café contribuye al cáncer,
¿no deberíamos tener cifras de cáncer mucho más altas que el 0,5 %? ¿No estaría más cerca del
65%?
Esto es lo engañoso de los números proporcionales. Pueden parecer significativos sin ningún
contexto dado, y los titulares de los medios ciertamente pueden explotar esto para obtener clics:
"Nuevo estudio revela que el 85% de los pacientes con cáncer beben café", podría leerse. Por
supuesto, esto es una tontería porque hemos tomado un atributo común (beber café) y lo hemos
asociado con uno poco común (tener cáncer).
La razón por la que las personas pueden confundirse tan fácilmente con las probabilidades
condicionales es porque la dirección de la condición importa, y las dos condiciones se combinan
como si fueran iguales. La “probabilidad de tener cáncer dado que es un bebedor de café” es
diferente de la “probabilidad de ser un bebedor de café dado que tiene cáncer”. En pocas palabras:
pocos bebedores de café tienen cáncer, pero muchos pacientes con cáncer beben café.
Si estamos interesados en estudiar si el café contribuye al cáncer, realmente nos interesa la
primera probabilidad condicional: la probabilidad de que alguien tenga cáncer dado que es un
bebedor de café.
P(Coffee|Cancer) = .85¿
P(Cancer|Coffee) =?
Cómo cambiamos la condición? Hay una pequeña y poderosa fórmula llamada Teorema de Bayes,
y podemos usarla para cambiar las probabilidades condicionales:
39
Si desea calcular esto en Python, consulte el Ejemplo 2-1.
Ejemplo 2-1. Usando el teorema de Bayes en Python
p_coffee_drinker = .65
p_cancer = .005
p_coffee_drinker_given_cancer = .85
p_cancer_given_coffee_drinker = p_coffee_drinker_given_cancer *
p_cancer / p_coffee_drinker
# prints 0.006538461538461539
print(p_cancer_given_coffee_drinker)¡
Entonces la probabilidad de que alguien tenga cáncer dado que es un bebedor de café es solo del
0.65%! Este número es muy diferente de la probabilidad de que alguien sea un bebedor de café
dado que tiene cáncer, que es del 85 %. ¿Ahora ves por qué es importante la dirección de la
condición? El teorema de Bayes es útil por esta razón. También se puede usar para encadenar
varias probabilidades condicionales para seguir actualizando nuestras creencias en función de
nueva información.
Si desea explorar la intuición detrás del Teorema de Bayes más profundamente, consulte el
Apéndice A. Por ahora, solo sé que nos ayuda a cambiar una probabilidad condicional. A
continuación, hablemos de cómo las probabilidades condicionales interactúan con las operaciones
conjuntas y de unión.
NAIVE BAYES
El teorema de Bayes juega un papel central en un algoritmo común de aprendizaje automático
llamado Naive Bayes. Joel Grus lo cubre en su libro Data Science from Scratch (O'Reilly).
Option 2:
P(Coffee|Cancer) × P(Cancer) = .85 × .005 = .00425
40
Si ya hemos establecido que nuestra probabilidad se aplica solo a personas con cáncer, ¿no tiene
sentido usar P (Café|Cáncer) en lugar de P (Café) ? Uno es más específico y se aplica a una
condición que ya se ha establecido. Entonces deberíamos usar P ( Coffee|Cancer ) ya que P
( Cancer ) ya es parte de nuestra probabilidad conjunta. Esto quiere decir que la probabilidad de
que alguien tenga cáncer y sea bebedor de café es del 0,425%:
P(Coffee and Cancer) = P(Coffee|Cancer) × P(Cancer) = .85 × .005 = .00425
Esta probabilidad conjunta también se aplica en la otra dirección. Puedo encontrar la probabilidad
de que alguien sea un bebedor de café y tenga cáncer al multiplicar P (Cáncer|Café) y P (Café).
Como puedes observar, llego a la misma respuesta:
P(Cancer|Coffee) × P(Coffee) = .0065 × .65 = .00425
Si no tuviéramos ninguna probabilidad condicional disponible, entonces lo mejor que podemos
hacer es multiplicar P ( Bebedor de café ) y P (Cáncer) como se muestra aquí:
P(Coffee Drinker) × P(Cancer) = .65 × .005 = .00325
Ahora piense en esto: si el evento A no tiene impacto en el evento B, ¿qué significa eso para la
probabilidad condicional P ( B | A )? Eso significa que P ( B | A ) = P ( B ), lo que significa que la
ocurrencia del evento A no hace ninguna diferencia en la probabilidad de que ocurra el evento B.
Por lo tanto, podemos actualizar nuestra fórmula de probabilidad conjunta, independientemente de
si los dos eventos son dependientes, para que sea:
P(A AND B) = P(B) × P(A|B)
Y finalmente hablemos de uniones y probabilidad condicional. Si quisiera calcular la probabilidad de
que ocurra A o B, pero A puede afectar la probabilidad de B, actualizamos nuestra regla de suma
de esta manera:
P(A OR B) = P(A) + P(B) - P(A|B) × P(B)
Como recordatorio, esto también se aplica a eventos mutuamente excluyentes. La regla de la suma
P ( A | B ) × P ( B ) arrojaría 0 si los eventos A y B no pueden ocurrir simultáneamente.
Distribución binomial
En el resto de este capítulo, aprenderemos dos distribuciones de probabilidad: las distribuciones
binomial y beta. Si bien no los usaremos en el resto del libro, son herramientas útiles en sí mismas
y fundamentales para comprender cómo ocurren los eventos después de una serie de pruebas.
También serán una buena transición para comprender las distribuciones de probabilidad que
usaremos mucho en el Capítulo 3. Exploremos un caso de uso que podría ocurrir en un escenario
del mundo real.
Supongamos que está trabajando en un nuevo motor a reacción de turbina y realizó 10 pruebas.
Los resultados arrojaron ocho éxitos y dos fracasos:
✓✓✓✓✓✘✓✘✓✓
Esperaba obtener una tasa de éxito del 90 %, pero según estos datos concluye que sus pruebas
fallaron con solo un 80 % de éxito. Cada prueba lleva mucho tiempo y es costosa, por lo que decide
que es hora de volver a la mesa de dibujo para rediseñar el diseño.
Sin embargo, uno de sus ingenieros insiste en que debería haber más pruebas. “La única manera
de saberlo con certeza es realizar más pruebas”, argumenta. “¿Qué pasa si más pruebas arrojan
un 90% o más de éxito? Después de todo, si lanzas una moneda 10 veces y obtienes 8 caras, no
significa que la moneda esté fijada en un 80 %”.
Consideras brevemente el argumento de la ingeniera y te das cuenta de que tiene razón. Incluso un
lanzamiento de moneda justo no siempre tendrá un resultado dividido por igual, especialmente con
solo 10 lanzamientos. Lo más probable es que obtenga cinco cabezas, pero también puede obtener
tres, cuatro, seis o siete cabezas. Incluso podría obtener 10 cabezas, aunque esto es
extremadamente improbable. Entonces, ¿cómo se determina la probabilidad de un 80 % de éxito
asumiendo que la probabilidad subyacente es del 90 %?
Una herramienta que podría ser relevante aquí es la distribución binomial, que mide la probabilidad
de que se produzcan k éxitos en n intentos dada una probabilidad p.
41
Visualmente, una distribución binomial se parece a la Figura 2-1.
Aquí, vemos la probabilidad de éxitos k para cada barra de 10 intentos. Esta distribución binomial
supone una probabilidad p del 90 %, lo que significa que hay una probabilidad de 0,90 (o 90 %) de
que se produzca el éxito. Si esto es cierto, eso significa que hay una probabilidad de.1937 de que
obtendríamos 8 éxitos de 10 intentos. La probabilidad de obtener 1 éxito de 10 intentos es
extremadamente improbable,.000000008999, por lo que la barra ni siquiera es visible.
También podemos calcular la probabilidad de ocho o menos éxitos sumando barras para ocho o
menos éxitos. Esto nos daría una probabilidad de.2639 de ocho o menos éxitos.
Entonces, ¿cómo implementamos la distribución binomial? Podemos hacerlo desde cero con
relativa facilidad (como se comparte en el Apéndice A ), o podemos usar bibliotecas como SciPy. El
ejemplo 2-2 muestra cómo usamos la función binom.pmf() de SciPy ( PMF significa "función de
masa de probabilidad") para imprimir las 11 probabilidades para nuestra distribución binomial de 0
a 10 éxitos.
Ejemplo 2-2. Usando SciPy para la distribución binomial
from scipy.stats import binom
n = 10
p = 0.9
for k in range(n + 1):
probability = binom.pmf(k, n, p)
print("{0} - {1}".format(k, probability))
# OUTPUT:
# 0 - 9.99999999999996e-11
# 1 - 8.999999999999996e-09
# 2 - 3.644999999999996e-07
# 3 - 8.748000000000003e-06
# 4 - 0.0001377809999999999
# 5 - 0.0014880347999999988
# 6 - 0.011160260999999996
42
# 7 - 0.05739562800000001
# 8 - 0.19371024449999993
# 9 - 0.38742048900000037
# 10 - 0.34867844010000004
Como puede ver, proporcionamos n como el número de intentos, p como la probabilidad de éxito
de cada intento y k como el número de éxitos para los que queremos buscar la probabilidad.
Iteramos cada número de éxitos x con la probabilidad correspondiente de que veríamos muchos
éxitos. Como podemos ver en la salida, el número más probable de éxitos es nueve.
Pero si sumamos la probabilidad de ocho o menos éxitos, obtendríamos.2639. Esto significa que
hay una probabilidad del 26,39 % de que veamos ocho éxitos o menos, incluso si la tasa de éxito
subyacente es del 90 %. Entonces, tal vez el ingeniero tenga razón: el 26,39% de probabilidad no
es nada y ciertamente es posible.
Sin embargo, hicimos una suposición aquí en nuestro modelo, que discutiremos a continuación con
la distribución beta.
Distribución beta
¿Qué asumí con mi modelo de prueba de motor utilizando la distribución binomial? ¿Hay algún
parámetro que supuse que era cierto y luego construí todo mi modelo a su alrededor? Piensa con
cuidado y sigue leyendo.
Lo que podría ser problemático acerca de mi distribución binomial es que asumí que la tasa de
éxito subyacente es del 90 %. Eso no quiere decir que mi modelo sea inútil. Acabo de mostrar que
si la tasa de éxito subyacente es del 90 %, hay un 26,39 % de posibilidades de que vea 8 éxitos o
menos con 10 intentos. Por lo tanto, el ingeniero ciertamente no está equivocado en cuanto a que
podría haber una tasa de éxito subyacente del 90 %.
Pero cambiemos la pregunta y consideremos esto: ¿qué pasa si hay otras tasas subyacentes de
éxito que producirían 8/10 éxitos además del 90 %? ¿Podríamos ver 8/10 éxitos con una tasa de
éxito subyacente del 80 %? 70%? 30%? Cuando arreglamos los 8/10 éxitos, ¿podemos explorar
las probabilidades de las probabilidades?
En lugar de crear innumerables distribuciones binomiales para responder a esta pregunta, hay una
herramienta que podemos usar. La distribución beta nos permite ver la probabilidad de diferentes
probabilidades subyacentes de que ocurra un evento dados los éxitos alfa y los fracasos beta.
En la Figura 2-2 se muestra un gráfico de la distribución beta con ocho éxitos y dos fracasos.
43
Figura 2-2. distribución beta
44
Figura 2-3. El área entre 90% y 100%, que es 22.5%
Como hicimos con la distribución binomial, podemos usar SciPy para implementar la distribución
beta. Cada distribución de probabilidad continua tiene una función de densidad acumulada (CDF),
que calcula el área hasta un valor x dado. Digamos que quisiera calcular el área hasta el 90 % (0,0
a 0,90) como se muestra en la figura 2-4.
Es bastante fácil usar SciPy con su función beta.cdf() y los únicos parámetrosNecesito proporcionar
el valor x, la cantidad de éxitos a y la cantidad de fallas b como se muestra en el Ejemplo 2-3.
Ejemplo 2-3. Distribución beta usando SciPy
from scipy.stats import beta
a=8
b=2
p = beta.cdf(.90, a, b)
# 0.7748409780000001
print(p)
De acuerdo con nuestro cálculo, existe un 77,48 % de posibilidades de que la probabilidad
subyacente de éxito sea del 90 % o menos.
45
¿Cómo calculamos que la probabilidad de éxito sea del 90 % o más, como se muestra en la figura
2-5 ?
Nuestro CDF calcula el área solo a la izquierda de nuestro límite, no a la derecha. Piensa en
nuestras reglas de probabilidad, y con una distribución de probabilidad el área total bajo la curva es
1.0. Si queremos encontrar la probabilidad opuesta de un evento (mayor que 0,90 en lugar de
menor que 0,90), simplemente reste la probabilidad de que sea menor que 0,90 de 1,0, y la
probabilidad restante capturará que sea mayor que 0,90. La figura 2-6 ilustra cómo hacemos esta
resta.
Esto significa que de 8/10 pruebas de motor exitosas, solo hay un 22,5 % de posibilidades de que
la tasa de éxito subyacente sea del 90 % o más. Pero hay un 77,5 % de probabilidad de que sea
46
menos del 90 %. Las probabilidades no están a nuestro favor aquí de que nuestras pruebas hayan
tenido éxito, pero podríamos apostar a esa probabilidad del 22,5% con más pruebas si nos
sentimos afortunados. Si nuestro CFO concediera fondos para 26 pruebas más que resultaran en
30 éxitos y 6 fracasos, nuestra distribución beta se vería como la Figura 2-7.
Observe que nuestra distribución se hizo más estrecha, por lo que se volvió más seguro de que la
tasa subyacente de éxito está en un rango más pequeño. Desafortunadamente, nuestra
probabilidad de cumplir con nuestra tasa mínima de éxito del 90 % ha disminuido, pasando del 22,5
% al 13,16 %, como se muestra en el ejemplo 2-5.
Ejemplo 2-5. Una distribución beta con más pruebas
from scipy.stats import beta
a = 30
b=6
p = 1.0 - beta.cdf(.90, a, b)
# 0.13163577484183708
print(p)
En este punto, podría ser una buena idea alejarse y dejar de hacer pruebas, a menos que quiera
seguir apostando contra esa probabilidad del 13,16 % y esperar que el pico se mueva hacia la
derecha.
Por último, pero no menos importante, ¿cómo calcularíamos un área en el medio? ¿Qué pasa si
quiero encontrar la probabilidad de que mi tasa subyacente de éxito esté entre 80% y 90% como se
muestra en la Figura 2-8 ?
47
Figura 2-8. Probabilidad de que la tasa subyacente de éxito esté entre el 80 % y el 90 %
Piense cuidadosamente cómo podría abordar esto. ¿Qué pasaría si restáramos el área detrás
de.80 del área detrás de.90 como en la figura 2-9 ?
¿Eso nos daría el área entre.80 y.90? Sí lo sería, y arrojaría un área de.3386 o 33.86% de
probabilidad. Así es como lo calcularíamos en Python ( Ejemplo 2-6 ).
Ejemplo 2-6. Área media de distribución beta usando SciPy
from scipy.stats import beta
a=8
b=2
p = beta.cdf(.90, a, b) - beta.cdf(.80, a, b)
# 0.33863336200000016
print(p)
La distribución beta es una herramienta fascinante para medir la probabilidad de que un evento
ocurra frente a que no ocurra, en función de un conjunto limitado de observaciones. Nos permite
razonar sobre probabilidades de probabilidades, y podemos actualizarlo a medida que obtenemos
48
nuevos datos. También podemos usarlo para probar hipótesis, pero pondremos más énfasis en el
uso de la distribución normal y la distribución T para ese propósito en el Capítulo 3.
DISTRIBUCIÓN BETA DESDE CERO
Para saber cómo implementar la distribución beta desde cero, consulte el Apéndice A.
Conclusión
¡Cubrimos mucho en este capítulo! No solo discutimos los fundamentos de la probabilidad, sus
operadores lógicos y el teorema de Bayes, sino que también presentamos las distribuciones de
probabilidad, incluidas las distribuciones binomial y beta. En el próximo capítulo cubriremos una de
las distribuciones más famosas, la distribución normal, y cómo se relaciona con la prueba de
hipótesis.
Si desea obtener más información sobre la probabilidad y las estadísticas bayesianas, un gran libro
es Bayesian Statistics the Fun Way de Will Kurt (No Starch Press). También hay escenarios
interactivos de Katacoda disponibles en la plataforma O'Reilly.
Ejercicios
1. Hay un 30 % de probabilidad de lluvia hoy y un 40 % de probabilidad de que su pedido de
paraguas llegue a tiempo. ¡Estás ansioso por caminar bajo la lluvia hoy y no puedes hacerlo
sin ninguno de los dos!
¿Cuál es la probabilidad de que llueva Y llegue tu paraguas?
2. Hay un 30 % de probabilidad de lluvia hoy y un 40 % de probabilidad de que su pedido de
paraguas llegue a tiempo.
Podrás hacer mandados solo si no llueve o llega tu paraguas.
3. ¿Cuál es la probabilidad de que no llueva O llegue tu paraguas?
Hay un 30 % de probabilidad de lluvia hoy y un 40 % de probabilidad de que su pedido de
paraguas llegue a tiempo.
Sin embargo, descubrió que si llueve, solo hay un 20% de posibilidades de que su paraguas
llegue a tiempo.
¿Cuál es la probabilidad de que llueva Y tu paraguas llegue a tiempo?
4. Tienes 137 pasajeros reservados en un vuelo de Las Vegas a Dallas. Sin embargo, es Las
Vegas un domingo por la mañana y usted estima que cada pasajero tiene un 40 % de
probabilidades de no presentarse.
Estás tratando de calcular cuántos asientos reservar para que el avión no vuele vacío.
¿Qué probabilidad hay de que al menos 50 pasajeros no se presenten?
5. Lanzaste una moneda 19 veces y obtuviste cara 15 veces y cruz 4 veces.
¿Crees que esta moneda tiene alguna buena probabilidad de ser justa? ¿Por qué o por qué no?
49
Capítulo 3. Estadística descriptiva e inferencial
Descriptive and Inferential Statistics La estadística es la práctica de recopilar y analizar datos para
descubrir hallazgos que sean útiles o predecir qué causa que esos hallazgos sucedan. La
probabilidad a menudo juega un papel importante en las estadísticas, ya que usamos datos para
estimar la probabilidad de que suceda un evento.
Puede que no siempre obtenga crédito, pero las estadísticas son el corazón de muchas
innovaciones basadas en datos. El aprendizaje automático en sí mismo es una herramienta
estadística que busca posibles hipótesis para correlacionar relaciones entre diferentes variables en
los datos. Sin embargo, hay muchos lados ciegos en las estadísticas, incluso para los estadísticos
profesionales. Podemos quedar atrapados fácilmente en lo que dicen los datos que olvidamos
preguntar de dónde provienen los datos. Estas preocupaciones se vuelven aún más importantes a
medida que los macrodatos, la minería de datos y el aprendizaje automático aceleran la
automatización de los algoritmos estadísticos. Por lo tanto, es importante tener una base sólida en
estadísticas y pruebas de hipótesis para no tratar estas automatizaciones como cajas negras.
En esta sección cubriremos los fundamentos de la estadística y la prueba de hipótesis.
Comenzando con estadísticas descriptivas, aprenderemos formas comunes de resumir datos.
Después de eso, nos aventuraremos en la estadística inferencial, donde trataremos de descubrir
los atributos de una población a partir de una muestra.
50
obtuvo una buena puntuación. ¡Tal vez puedas tomar una foto de su tarjeta de puntuación! Pero es
importante tener en cuenta que todos estos casos pueden falsificarse o sacarse de contexto. Tal
vez estaba animando a otra persona, o tal vez la tarjeta de puntuación no era suya o ni siquiera era
falsa. Al igual que estas fotografías, los datos no capturan el contexto ni las explicaciones. Este es
un punto increíblemente importante porque los datos proporcionan pistas, no la verdad. Estas
pistas pueden llevarnos a la verdad o llevarnos a conclusiones erróneas.
Esta es la razón por la que tener curiosidad sobre el origen de los datos es una habilidad tan
importante. Haga preguntas sobre cómo se crearon los datos, quién los creó y qué datos no
capturan. Es demasiado fácil quedar atrapado en lo que dicen los datos y olvidarse de preguntar de
dónde provienen. Peor aún, hay sentimientos generalizados de que uno puede introducir datos en
algoritmos de aprendizaje automático y esperar que la computadora lo resuelva todo. Pero como
dice el adagio, “si entra basura, sale basura”. No es de extrañar que solo el 13 % de los proyectos
de aprendizaje automático tengan éxito, según VentureBeat. Los proyectos exitosos de aprendizaje
automático ponen el pensamiento y el análisis en los datos, así como en lo que produjo los datos.
VERDAD BÁSICA
Hablando en términos más generales, el ejemplo de la foto familiar presenta un problema de
verdad básica.
Cuando estaba dando una clase sobre la seguridad del sistema de inteligencia artificial, una vez me
hicieron una pregunta sobre cómo hacer que los autos sin conductor sean más seguros. "Cuando
un automóvil autónomo no reconoce a un peatón en el sensor de su cámara, ¿no hay alguna forma
de que reconozca la falla y se detenga?" Respondí que no, porque el sistema no tiene un marco
para la verdad fundamental, o un conocimiento verificado y completo de lo que es true. Si el
automóvil no reconoce a un peatón, ¿cómo se supone que reconocerá que no reconoce a un
peatón? No hay una verdad básica a la que recurrir, a menos que haya un operador humano que
pueda proporcionarla e intervenir.
En realidad, este es el statu quo de los automóviles y los servicios de taxi "autónomos". Algunos
sensores, como el radar, brindan verdades sobre el terreno modestamente confiables en preguntas
limitadas, como "¿Hay algo frente al vehículo?" Pero reconocer objetos basados en cámaras y
sensores LIDAR (en un entorno no controlado) es un problema de percepción mucho más borroso
con números astronómicos de combinaciones de píxeles que podría ver. Por lo tanto, la verdad
fundamental es inexistente.
¿Sus datos representan una realidad básica verificable y completa? ¿Son los sensores y las
fuentes fiables y precisos? ¿O se desconoce la verdad básica??
51
de segundo año en la Escuela Secundaria Los Altos. Cómo perfeccionar la definición de una
población depende de lo que le interese estudiar.
Una muestra es un subconjunto de la población que es idealmente aleatorio e imparcial, que
usamos para inferir atributos sobre la población. A menudo tenemos que estudiar muestras porque
no siempre es posible encuestar a toda la población. Por supuesto, algunas poblaciones son más
fáciles de localizar si son pequeñas y accesibles. ¿Pero medir a todas las personas mayores de 65
años en América del Norte? Es poco probable que sea práctico!
52
La forma de superar este problema es seleccionar estudiantes verdaderamente al azar de toda la
población, y no pueden elegirse a sí mismos para entrar o salir de la muestra voluntariamente. Esta
es la forma más efectiva de mitigar el sesgo, pero como puede imaginar, se necesitan muchos
recursos coordinados para hacerlo.
53
Muy bien, basta de hablar sobre poblaciones, muestras y sesgos. Pasemos a algunas matemáticas
y estadísticas descriptivas. Solo recuerde que las matemáticas y las computadoras no reconocen el
sesgo en sus datos. ¡Eso depende de usted como buen profesional de la ciencia de datos para
detectar! Siempre haga preguntas sobre cómo se obtuvieron los datos y luego analice cómo ese
proceso podría haber sesgado los datos.
Estadísticas descriptivas
La estadística descriptiva es el área con la que la mayoría de la gente está familiarizada.
Tocaremos los conceptos básicos como la media, la mediana y la moda, seguidos de la varianza, la
desviación estándar y la distribución normal.
Recuerde que el símbolo de suma ∑ significa sumar todos los elementos. La n y la N representan
el tamaño de la muestra y la población, respectivamente, pero matemáticamente representan lo
mismo: el número de elementos. Lo mismo ocurre con llamar a la media muestral (“barra x”) y a
la media poblacional μ (“mu”). Tanto como μ son el mismo cálculo, solo nombres diferentes
según se trate de una muestra o de una población con la que estemos trabajando.
Es probable que la media le resulte familiar, pero aquí hay algo menos conocido sobre la media: la
media es en realidad un promedio ponderado llamado media ponderada. La media que usamos
comúnmente le da igual importancia a cada valor. Pero podemos manipular la media y darle a cada
elemento un peso diferente:
54
Esto puede ser útil cuando queremos que algunos valores contribuyan a la media más que otros.
Un ejemplo común de esto es la ponderación de los exámenes académicos para dar una
calificación final. Si tiene tres exámenes y un examen final, y le damos a cada uno de los tres
exámenes un 20 % de peso y al examen final un 40 % de peso de la calificación final, cómo lo
expresamos es en el Ejemplo 3-2.
Ejemplo 3-2. Cálculo de una media ponderada en Python
# Three exams of .20 weight each and final exam of .40 weight
sample = [90, 80, 63, 87]
weights = [.20, .20, .20, .40]
weighted_mean = sum(s * w for s,w in zip(sample, weights)) / sum(weights)
print(weighted_mean) # prints 81.4
Ponderamos la puntuación de cada examen a través de la multiplicación correspondiente y, en
lugar de dividir por el recuento de valores, dividimos por la suma de los pesos. Las ponderaciones
no tienen que ser porcentajes, ya que cualquier número que se use para ponderar terminará siendo
proporcional. En el Ejemplo 3-3, ponderamos cada examen con "1" pero ponderamos el examen
final con "2", dándole el doble del peso de los exámenes. Seguiremos obteniendo la misma
respuesta de 81,4 ya que estos valores seguirán siendo proporcionales.
Ejemplo 3-3. Cálculo de una media ponderada en Python
# Three exams of .20 weight each and final exam of .40 weight
sample = [90, 80, 63, 87]
weights = [1.0, 1.0, 1.0, 2.0]
weighted_mean = sum(s * w for s,w in zip(sample, weights)) / sum(weights)
print(weighted_mean) # prints 81.4
Mediana
La mediana es el valor más medio en un conjunto de valores ordenados. Ordenas secuencialmente
los valores, y la mediana será el valor más central. Si tiene un número par de valores, promedia los
dos valores más centrales. Podemos ver en el Ejemplo 3-4 que la mediana del número de
mascotas en nuestra muestra es 7:
0, 1, 5, *7*, 9, 10, 14
Ejemplo 3-4. Cálculo de la mediana en Python
# Number of pets each person owns
sample = [0, 1, 5, 7, 9, 10, 14]
def median(values):
ordered = sorted(values)
print(ordered)
n = len(ordered)
mid = int(n / 2) - 1 if n % 2 == 0 else int(n/2)
if n % 2 == 0:
return (ordered[mid] + ordered[mid+1]) / 2.0
else:
return ordered[mid]
print(median(sample)) # prints 7
La mediana puede ser una alternativa útil a la media cuando los datos están sesgados por valores
atípicos o valores que son extremadamente grandes y pequeños en comparación con el resto de
los valores. Aquí hay una anécdota interesante para entender por qué. En 1986, el salario inicial
medio anual de los graduados en geografía de la Universidad de Carolina del Norte en Chapel Hill
era de 250.000 dólares. Otras universidades promediaron $22,000. Wow, UNC-CH debe tener un
programa de geografía increíble!
Pero en realidad, ¿qué tenía de lucrativo el programa de geografía de la UNC? Bueno.. Michael
Jordan fue uno de sus graduados. Uno de los jugadores de la NBA más famosos de todos los
tiempos se graduó con un título en geografía de la UNC. Sin embargo, comenzó su carrera jugando
55
baloncesto, no estudiando mapas. Obviamente, esta es una variable de confusión que ha creado
un gran valor atípico y sesgó en gran medida el promedio de ingresos.
Esta es la razón por la que la mediana puede ser preferible en situaciones con muchos valores
atípicos (como los datos relacionados con los ingresos) sobre la media. Es menos sensible a los
valores atípicos y corta los datos estrictamente por la mitad en función de su orden relativo, en
lugar de dónde se encuentran exactamente en una recta numérica. Cuando su mediana es muy
diferente de su media, eso significa que tiene un conjunto de datos sesgado con valores atípicos.
LA MEDIANA ES UN CUANTIL
Existe un concepto de cuantiles en estadística descriptiva. El concepto de cuantiles es
esencialmente el mismo que una mediana, simplemente cortando los datos en otros lugares
además del medio. La mediana es en realidad el cuantil del 50 %, o el valor detrás del cual se
encuentra el 50 % de los valores ordenados. Luego están los cuantiles del 25 %, 50 % y 75 %, que
se conocen como cuartiles porque cortan los datos en incrementos del 25 %.
Moda
La moda es el conjunto de valores que ocurre con más frecuencia. Principalmente se vuelve útil
cuando sus datos son repetitivos y desea encontrar qué valores ocurren con mayor frecuencia.
Cuando ningún valor aparece más de una vez, no hay moda. Cuando dos valores ocurren con la
misma frecuencia, el conjunto de datos se considera bimodal. En el Ejemplo 3-5, calculamos la
moda para nuestro conjunto de datos de mascotas y, efectivamente, vemos que esto es bimodal,
ya que tanto 2 como 3 ocurren con la mayor frecuencia (y con la misma frecuencia).
Ejemplo 3-5. Calculando el modo en Python
# Number of pets each person owns
from collections import defaultdict
sample = [1, 3, 2, 5, 7, 0, 2, 3]
def mode(values):
counts = defaultdict(lambda: 0)
for s in values:
counts[s] += 1
max_count = max(counts.values())
modes = [v for v in set(values) if counts[v] == max_count]
return modes
print(mode(sample)) # [2, 3]
En la práctica, el modo no se usa mucho a menos que sus datos sean repetitivos. Esto se
encuentra comúnmente con números enteros, categorías y otras variables discretas.
56
5 6.571 -1.571
7 6.571 0.429
9 6.571 2.429
10 6.571 3.429
14 6.571 7.429
Visualicemos esto en una recta numérica con “X” mostrando la media en la Figura 3-1.
Hmmm.. ahora considere por qué esta información puede ser útil. Las diferencias nos dan una idea
de qué tan dispersos están los datos y qué tan lejos están los valores de la media. ¿Hay alguna
manera de consolidar estas diferencias en un solo número para describir rápidamente qué tan
dispersos están los datos?
Es posible que tengas la tentación de sacar el promedio de las diferencias, pero los negativos y los
positivos se cancelarán entre sí cuando se sumen. Podríamos sumar los valores absolutos
(eliminar los signos negativos y hacer que todos los valores sean positivos). Un enfoque aún mejor
sería elevar al cuadrado estas diferencias antes de sumarlas. Esto no solo elimina los valores
negativos (porque elevar al cuadrado un número negativo lo convierte en positivo), sino que
amplifica diferencias más grandes y es matemáticamente más fácil trabajar con ellas (las derivadas
no son sencillas con valores absolutos). Después de eso, promedie las diferencias al cuadrado.
Esto nos dará la varianza, una medida de cuán dispersos están nuestros datos.
Aquí hay una fórmula matemática que muestra cómo calcular la varianza: varianza poblacional
57
El opuesto de un cuadrado es una raíz cuadrada, así que tomemos la raíz cuadrada de la varianza,
que nos da la desviación estándar. Esta es la variación escalada en un número expresado en
términos de "número de mascotas", lo que lo hace un poco más significativo.:
Para implementar en Python, podemos reutilizar la función variance() y sqrt() su resultado. Ahora
tenemos una función std_dev(), que se muestra en el Ejemplo 3-7.
Ejemplo 3-7. Cálculo de la desviación estándar en Python
from math import sqrt
# Number of pets each person owns
data = [0, 1, 5, 7, 9, 10, 14]
def variance(values):
mean = sum(values) / len(values)
_variance = sum((v - mean) ** 2 for v in values) / len(values)
return _variancedef std_dev(values):
def std_dev(values):
return sqrt(variance(values))
print(std_dev(data)) # prints 4.624689730353898
Si los datos de nuestras mascotas fueran una muestra, no una población, deberíamos hacer ese
ajuste en consecuencia. En el Ejemplo 3-8, modifico mi código anterior de Python variance() y
58
std_dev() para proporcionar opcionalmente un parámetro is_sample, que si es True restará 1 del
divisor en la varianza.
Observe en el Ejemplo 3-8 que mi varianza y desviación estándar han aumentado en comparación
con los ejemplos anteriores que los trataban como una población, no como una muestra. Recuerde
en el Ejemplo 3-7 que la desviación estándar fue de aproximadamente 4.62 tratándose como una
población. Pero aquí, tratándolo como una muestra (al restar 1 del denominador de la varianza),
obtenemos aproximadamente 4,99. Esto es correcto ya que una muestra podría estar sesgada e
imperfecta al representar a la población. Por lo tanto, aumentamos la varianza (y, por lo tanto, la
desviación estándar) para aumentar nuestra estimación de cuán dispersos están los valores. Una
varianza/desviación estándar más grande muestra menos confianza con un rango más grande.
Al igual que la media ( x ¯ para la muestra y μ para la población), a menudo verá ciertos
símbolos para la varianza y la desviación estándar. La desviación estándar para una muestra y la
media se especifican mediante s y σ, respectivamente. Aquí nuevamente están las fórmulas de
desviación estándar de la muestra y la población:
La varianza será el cuadrado de estas dos fórmulas, deshaciendo la raíz cuadrada. Por lo tanto, la
varianza para la muestra y la población son s2 y σ2, respectivamente:
Nuevamente, el cuadrado ayuda a implicar que se debe tomar una raíz cuadrada para
obtener la desviación estándar.
La distribución normal
Hablamos de las distribuciones de probabilidad en el último capítulo, en particular la
distribución binomial y la distribución beta. Sin embargo, la distribución más famosa de
todas es la distribución normal. La distribución normal, también conocida como distribución
59
gaussiana, es una distribución simétrica en forma de campana que tiene la mayor parte de
la masa alrededor de la media y su dispersión se define como una desviación estándar.
Las "colas" a ambos lados se vuelven más delgadas a medida que se aleja de la media.
La figura 3-2 es una distribución normal para los pesos de los golden retrievers. Observe
cómo la mayor parte de la masa está alrededor de la media de 64,43 libras.
Observe cómo tenemos más valores hacia el centro, pero a medida que nos movemos más hacia
la izquierda o hacia la derecha vemos menos valores. Según nuestra muestra, parece muy poco
probable que veamos un golden retriever con un peso de 57 o 71. ¿Pero con un peso de 64 o 65?
Sí, eso ciertamente parece probable.
¿Hay una mejor manera de visualizar esta probabilidad de ver qué pesos de golden retriever es
más probable que veamos muestreados de la población? Podemos intentar crear un histograma,
que agrupe (o "clasifique") valores en función de rangos numéricos de igual longitud, y luego use
un gráfico de barras que muestre la cantidad de valores dentro de cada rango. En la Figura 3-4
creamos un histograma que agrupa valores en rangos de 0,5 libras.
Este histograma no revela ninguna forma significativa para nuestros datos. La razón es que
nuestros contenedores son demasiado pequeños. No tenemos una cantidad de datos
extremadamente grande o infinita para tener suficientes puntos en cada contenedor. Por lo tanto,
tendremos que hacer nuestros contenedores más grandes. Hagamos que los contenedores tengan
cada uno una longitud de tres libras, como en la Figura 3-5.
60
Figura 3-4. Un histograma de pesos de golden retriever
¡Ahora estamos llegando a alguna parte! Como puede ver, si obtenemos los tamaños de
contenedor correctos (en este caso, cada uno tiene un rango de tres libras), comenzamos a
obtener una forma de campana significativa para nuestros datos. No es una forma de campana
perfecta porque nuestras muestras nunca serán perfectamente representativas de la población,
pero es probable que esto sea evidencia de que nuestra muestra sigue una distribución normal. Si
ajustamos un histograma con tamaños de contenedores adecuados y lo escalamos para que tenga
un área de 1,0 (lo que requiere una distribución de probabilidad), vemos una curva de campana
aproximada que representa nuestra muestra. Mostrémoslo junto con nuestros puntos de datos
originales en la Figura 3-6.
Mirando esta curva de campana, podemos esperar razonablemente que un golden retriever tenga
un peso de alrededor de 64.43 (la media) pero poco probable de 55 o 73. Cualquier cosa más
extrema que eso se vuelve muy poco probable.
61
Figura 3-6. Una distribución normal ajustada a puntos de datos
Wow, eso es un bocado, ¿no? Incluso vemos el Número e de nuestro amigo Euler del Capítulo 1
y algunos exponentes locos. Así es como podemos expresarlo en Python en el Ejemplo 3-9.
Ejemplo 3-9. La función de distribución normal en Python
# normal distribution, returns likelihood
def normal_pdf(x: float, mean: float, std_dev: float) -> float:
return (1.0 / (2.0 * math.pi * std_dev ** 2) ** 0.5) *
math.exp(-1.0 * ((x - mean) ** 2 / (2.0 * std_dev ** 2)))
Hay mucho que desglosar aquí en esta fórmula, pero lo importante es que acepta una media y una
desviación estándar como parámetros, así como un valor x para que pueda buscar la probabilidad
en ese valor dado.
Al igual que la distribución beta del Capítulo 2, la distribución normal es continua. Esto significa que
para recuperar una probabilidad necesitamos integrar un rango de valores de x para encontrar un
área.
Sin embargo, en la práctica, usaremos SciPy para hacer estos cálculos por nosotros.
62
curva para ese rango. Digamos que quiero encontrar la probabilidad de que un golden retriever
pese entre 62 y 66 libras. La figura 3-7 muestra el rango para el que queremos encontrar el área.
Ya hicimos esta tarea en el Capítulo 2 con la distribución beta y, al igual que la distribución beta,
existe una función de densidad acumulada (CDF cumulative density function). Sigamos este
enfoque.
Como aprendimos en el último capítulo, la CDF proporciona el área hasta un valor x dado para una
distribución dada. Veamos cómo se ve el CDF para nuestra distribución normal de golden retriever
y colóquelo junto al PDF como referencia en la Figura 3-8.
Observe que hay una relación entre los dos gráficos. La CDF, que es una curva en forma de S
(llamada curva sigmoidea), proyecta el área hasta ese rango en la PDF. Observe en la Figura 3-9
que cuando capturamos el área desde el infinito negativo hasta 64,43 (la media), nuestra CDF
muestra un valor de exactamente 0,5 o 50 %.!
63
Figura 3-9. Un PDF y CDF para los pesos de golden retriever que miden la probabilidad hasta la
media
Esta área de 0,5 o 50 % hasta la media se conoce debido a la simetría de nuestra distribución
normal, y podemos esperar que el otro lado de la curva de campana también tenga el 50 % del
área.
Para calcular esta área hasta 64,43 en Python usando SciPy, use la función norm.cdf() como se
muestra en el Ejemplo 3-10.
Ejemplo 3-10. La distribución normal CDF en Python
from scipy.stats import norm
mean = 64.43
std_dev = 2.99
64
Hacer esto en Python usando SciPy es tan simple como restar las dos operaciones CDF que se
muestran en el Ejemplo 3-11.
Ejemplo 3-11. Obtener una probabilidad de rango medio usando la CDF
from scipy.stats import norm
mean = 64.43
std_dev = 2.99
Debe encontrar que la probabilidad de observar un golden retriever entre 62 y 66 libras es 0.4920,
o aproximadamente 49.2%.
La CDF inversa
Cuando comencemos a hacer pruebas de hipótesis más adelante en este capítulo, encontraremos
situaciones en las que necesitamos buscar un área en la CDF y luego devolver el valor x
correspondiente. Por supuesto, este es un uso inverso de la CDF, por lo que necesitaremos usar la
CDF inversa, que invierte los ejes como se muestra en la Figura 3-11.
De esta forma, ahora podemos buscar una probabilidad y luego devolver el valor x correspondiente,
y en SciPy usaríamos la función norm.ppf(). Por ejemplo, quiero encontrar el peso en el que se
encuentran el 95 % de los golden retrievers. Esto es fácil de hacer cuando uso la CDF inversa en el
ejemplo 3-12.
Encuentro que el 95% de los golden retrievers pesan 69.348 libras o menos.
También puede usar la CDF inversa para generar números aleatorios que sigan la distribución
normal. Si quiero crear una simulación que genere mil pesos realistas de golden retriever, solo
genero un valor aleatorio entre 0.0 y 1.0, lo paso a la CDF inversa y devuelvo el valor del peso
como se muestra en el Ejemplo 3-13.
Ejemplo 3-13. Generación de números aleatorios a partir de una distribución normal
import random
from scipy.stats import norm
for i in range(0,1000):
random_p = random.uniform(0.0, 1.0)
random_weight = norm.ppf(random_p, loc=64.43, scale=2.99)
print(random_weight)
65
Por supuesto, NumPy y otras bibliotecas pueden generar valores aleatorios a partir de una
distribución para usted, pero esto destaca un caso de uso en el que la CDF inversa es útil.
Puntuaciones Z
Z-Scores Es común cambiar la escala de una distribución normal para que la media sea 0 y la
desviación estándar sea 1, lo que se conoce como distribución normal estándar. Esto facilita la
comparación de la dispersión de una distribución normal con otra distribución normal, incluso si
tienen diferentes medias y varianzas.
De particular importancia con la distribución normal estándar es que expresa todos los valores de x
en términos de desviaciones estándar, conocidas como puntuaciones Z. Convertir un valor x en una
puntuación Z utiliza una fórmula de escala básica:
Aquí hay un ejemplo. Tenemos dos viviendas de dos barrios diferentes. El vecindario A tiene un
valor promedio de vivienda de $140,000 y una desviación estándar de $3,000. El vecindario B tiene
un valor medio de vivienda de $800 000 y una desviación estándar de $10 000.
μA = 140,000
μB = 800,000
σA = 3,000
σB = 10,000
Ahora tenemos dos casas de cada barrio. La casa A del barrio A vale $150 000 y la casa B del
barrio B vale $815 000. ¿Qué casa es más cara en relación con la casa promedio en su vecindario?
xA = 150,000
xB = 815,000
Si expresamos estos dos valores en términos de desviaciones estándar, podemos compararlos en
relación con la media de cada vecindario. Utilice la fórmula de puntuación Z:
Entonces, la casa en el vecindario A es en realidad mucho más cara en relación con su vecindario
que la casa en el vecindario B, ya que tienen puntajes Z de y 1.5, respectivamente.
Así es como podemos convertir un valor de x proveniente de una distribución dada con una media y
una desviación estándar en un puntaje Z, y viceversa, como se muestra en el Ejemplo 3-14.
Ejemplo 3-14. Convierta las puntuaciones Z en valores x y viceversa
def z_score(x, mean, std):
return (x - mean) / std
def z_to_x(z, mean, std):
return (z * std) + mean
mean = 140000
std_dev = 3000
x = 150000
# Convert to Z-score and then back to X
z = z_score(x, mean, std_dev)
66
back_to_x = z_to_x(z, mean, std_dev)
print("Z-Score: {}".format(z)) # Z-Score: 3.333
print("Back to X: {}".format(back_to_x)) # Back to X: 150000.0
COEFICIENTE DE VARIACIÓN
Una herramienta útil para medir la dispersión es el coeficiente de variación. Compara dos
distribuciones y cuantifica la dispersión de cada una de ellas. Es simple de calcular: divide la
desviación estándar por la media. Aquí está la fórmula junto con el ejemplo que compara dos
vecindarios:
Como se ve aquí, el barrio A, aunque es más barato que el barrio B, tiene más dispersión y, por lo
tanto, más diversidad de precios que el barrio B.
Estadística inferencial
Inferential Statistics La estadística descriptiva, que hemos cubierto hasta ahora, se entiende
comúnmente. Sin embargo, cuando nos adentramos en la estadística inferencial, las relaciones
abstractas entre la muestra y la población entran en pleno juego. Estos matices abstractos no son
algo por lo que quieras apresurarte, sino que te tomes tu tiempo y los absorbas cuidadosamente.
Como se indicó anteriormente, estamos programados como humanos para ser parciales y llegar
rápidamente a conclusiones. Ser un buen profesional de la ciencia de datos requiere que suprimas
ese deseo primario y consideres la posibilidad de que puedan existir otras explicaciones. Es
aceptable (quizás incluso ilustrado) teorizar que no hay ninguna explicación y que un hallazgo es
solo una coincidencia y al azar.
Primero, comencemos con el teorema que sienta las bases para todas las estadísticas
inferenciales.
67
sample_size)
for _ in range(sample_count)]
y_values = [1 for _ in range(sample_count)]
px.histogram(x=x_values, y = y_values, nbins=20).show()
Figura 3-12. Tomar las medias de las muestras (cada una de tamaño 31) y graficarlas
Espere, ¿cómo los números aleatorios uniformes, cuando se muestrearon como grupos de 31 y
luego se promediaron, formaron aproximadamente una distribución normal? Cualquier número es
igualmente probable, ¿verdad? ¿No debería la distribución ser plana en lugar de curva de
campana?
Esto es lo que está sucediendo. Los números individuales en las muestras por sí solos no crearán
una distribución normal. La distribución será plana donde cualquier número sea igualmente
probable (lo que se conoce como distribución uniforme ). Pero cuando los agrupamos como
muestras y los promediamos, forman una distribución normal.
Esto se debe al teorema del límite central, que establece que suceden cosas interesantes cuando
tomamos muestras lo suficientemente grandes de una población, calculamos la media de cada una
y las representamos como una distribución:
1. La media de las medias muestrales es igual a la media poblacional.
2. Si la población es normal, entonces las medias muestrales serán normales.
3. Si la población no es normal, pero el tamaño de la muestra es mayor que 30, las medias
de la muestra seguirán formando aproximadamente una distribución normal.
4. La desviación estándar de las medias de la muestra es igual a la desviación estándar de
la población dividida por la raíz cuadrada de n :
¿Por qué es importante todo lo anterior? Estos comportamientos nos permiten inferir cosas útiles
sobre poblaciones basadas en muestras, incluso para poblaciones no normales. Si modifica el
código anterior y prueba tamaños de muestra más pequeños de 1 o 2, no verá surgir una
distribución normal. Pero a medida que se acerca a 31 o más, verá que convergemos en una
distribución normal, como se muestra en la figura 3-13.
68
Figura 3-13. Los tamaños de muestra más grandes se aproximan a la distribución normal
Treinta y uno es el número de libro de texto en estadística porque es cuando nuestra distribución
de muestra a menudo converge en la distribución de población, particularmente cuando medimos
medias de muestra u otros parámetros. Cuando tiene menos de 31 elementos en su muestra, es
cuando tiene que confiar en la distribución T en lugar de la distribución normal, que tiene colas
cada vez más anchas cuanto más pequeño es el tamaño de la muestra. Hablaremos brevemente
de esto más adelante, pero primero supongamos que tenemos al menos 31 elementos en nuestras
muestras cuando hablamos de intervalos de confianza y pruebas.
Intervalos de confianza
Es posible que haya escuchado el término "intervalo de confianza", que a menudo confunde a los
recién llegados a la estadística y a los estudiantes. Un intervalo de confianza es un cálculo de
rango que muestra con qué confianza creemos que la media de una muestra (u otro parámetro)
cae en un rango para la media de la población.
Con base en una muestra de 31 golden retrievers con una media muestral de 64,408 y una
desviación estándar muestral de 2,05, estoy 95 % seguro de que la media poblacional se
encuentra entre 63,686 y 65,1296. ¿Cómo sé esto? Déjame mostrarte, y si te confundes, regresa a
este párrafo y recuerda lo que estamos tratando de lograr. Lo destaqué por una razón.!
Primero comienzo eligiendo un nivel de confianza (LOC level of confidence), que contendrá la
probabilidad deseada para el rango medio de la población. Quiero estar 95 % seguro de que la
media de mi muestra se encuentra en el rango de la media de la población que calcularé. Ese es mi
LOC. Podemos aprovechar el teorema del límite central e inferir cuál es este rango para la media
de la población. Primero, necesito el valor z crítico, que es el rango simétrico en una distribución
normal estándar que me da una probabilidad del 95 % en el centro, como se destaca en la Figura
3-14.
¿Cómo calculamos este rango simétrico que contiene.95 del área? Es más fácil de entender como
concepto que como cálculo. Es posible que instintivamente desee utilizar el CDF, pero luego puede
darse cuenta de que hay algunas partes móviles más aquí.
Primero necesitas aprovechar el CDF inverso. Lógicamente, para obtener el 95% del área simétrica
en el centro, cortaríamos las colas que tienen el 5% restante del área. Dividir el 5 % restante del
área por la mitad nos daría un 2,5 % de área en cada cola. Por lo tanto, las áreas para las que
queremos buscar los valores de x son.025 y.975, como se muestra en la Figura 3-15.
69
Figura 3-14. 95% de probabilidad simétrica en el centro de una distribución normal estándar
Figura 3-15. Queremos los valores de x que nos den áreas.025 y.975
Podemos buscar el valor x para el área 0,025 y el valor x para el área 0,975, y eso nos dará
nuestro rango central que contiene el 95 % del área. Luego devolveremos los valores z inferior y
superior correspondientes que contienen esta área. Recuerde, estamos usando la distribución
normal estándar aquí, por lo que serán iguales, aparte de ser positivos/negativos. Calculemos esto
en Python como se muestra en el Ejemplo 3-16.
Ejemplo 3-16. Recuperación de un valor z crítico
from scipy.stats import norm
def critical_z_value(p):
norm_dist = norm(loc=0.0, scale=1.0)
left_tail_area = (1.0 - p) / 2.0
upper_area = 1.0 - ((1.0 - p) / 2.0)
return norm_dist.ppf(left_tail_area), norm_dist.ppf(upper_area)
print(critical_z_value(p=.95))
# (-1.959963984540054, 1.959963984540054)
Bien, entonces obtenemos ±1.95996, que es nuestro valor z crítico que captura el 95 % de
probabilidad en el centro de la distribución normal estándar. A continuación, aprovecharé el
teorema del límite central para producir el margen de error (E), que es el rango alrededor de la
media de la muestra que contiene la media de la población en ese nivel de confianza. Recuerde
que nuestra muestra de 31 golden retrievers tiene una media de 64,408 y una desviación estándar
de 2,05. La fórmula para obtener este margen de error es:
70
E = ±0.72164
Si aplicamos ese margen de error contra la media de la muestra, ¡finalmente obtenemos el intervalo
de confianza!
95% confidence interval = 64.408 ± 0.72164
Así es como calculamoseste intervalo de confianza en Python de principio a fin en el Ejemplo 3-17.
Ejemplo 3-17. Cálculo de un intervalo de confianza en Python
from math import sqrt
from scipy.stats import norm
def critical_z_value(p):
norm_dist = norm(loc=0.0, scale=1.0)
left_tail_area = (1.0 - p) / 2.0
upper_area = 1.0 - ((1.0 - p) / 2.0)
return norm_dist.ppf(left_tail_area), norm_dist.ppf(upper_area)
def confidence_interval(p, sample_mean, sample_std, n):
# Sample size must be greater than 30
lower, upper = critical_z_value(p)
lower_ci = lower * (sample_std / sqrt(n))
upper_ci = upper * (sample_std / sqrt(n))
return sample_mean + lower_ci, sample_mean + upper_ci
print(confidence_interval(p=.95, sample_mean=64.408, sample_std=2.05, n=31))
# (63.68635915701992, 65.12964084298008)
71
matemáticas combinatorias, la probabilidad de que Muriel haya acertado por completo las copas es
del 1,4 %. ¿Qué te dice eso exactamente?
Cuando enmarcamos un experimento, ya sea para determinar si las donas orgánicas causan
aumento de peso o si vivir cerca de las líneas eléctricas causa cáncer, siempre tenemos que
considerar la posibilidad de que la suerte aleatoria haya jugado un papel. Al igual que hay un 1,4 %
de posibilidades de que Muriel haya identificado correctamente las tazas de té simplemente
adivinando, siempre existe la posibilidad de que la aleatoriedad nos dé una buena mano como una
máquina tragamonedas. Esto nos ayuda a enmarcar nuestra hipótesis nula (H 0 ), diciendo que la
variable en cuestión no tuvo impacto en el experimento y que cualquier resultado positivo es solo
suerte aleatoria. La hipótesis alternativa (H 1 ) plantea que una variable en cuestión (llamada
variable controlada ) está provocando un resultado positivo.
Tradicionalmente, el umbral de significación estadística es un valor p del 5 % o menos, o 0,05.
Dado que.014 es menor que.05, esto significaría que podemos rechazar nuestra hipótesis nula de
que Muriel estaba adivinando al azar. Entonces podemos promover la hipótesis alternativa de que
Muriel tenía una habilidad especial para detectar si se sirvió té o leche primero.
Ahora, una cosa que este ejemplo de la fiesta del té no capturó es que cuando calculamos un valor
p, capturamos todas las probabilidades de ese evento o menos. Abordaremos esto a medida que
nos sumerjamos en el siguiente ejemplo usando la distribución normal.
Prueba de hipótesis
Estudios anteriores han demostrado que el tiempo medio de recuperación de un resfriado es de 18
días, con una desviación estándar de 1,5 días, y sigue una distribución normal.
Esto significa que hay aproximadamente un 95 % de posibilidades de que la recuperación tarde
entre 15 y 21 días, como se muestra en la Figura 3-16 y el Ejemplo 3-18.
72
Figura 3-17. Un grupo que tomaba una droga tardó 16 días en recuperarse
¿La droga tuvo un impacto? Si razona lo suficiente, puede darse cuenta de que lo que estamos
preguntando es esto: ¿el fármaco muestra un resultado estadísticamente significativo? ¿O el
medicamento no funcionó y la recuperación de 16 días fue una coincidencia con el grupo de
prueba? Esa primera pregunta enmarca nuestra hipótesis alternativa, mientras que la segunda
pregunta enmarca nuestra hipótesis nula.
Hay dos formas en que podemos calcular esto: la prueba de una cola y la de dos colas.
Comenzaremos con el de una cola.
Ejemplo 3-19. Código de Python para obtener el valor x con el 5 % del área detrás
from scipy.stats import norm
# Cold has 18 day mean recovery, 1.5 std dev
mean = 18
std_dev = 1.5
# What x-value has 5% of area behind it?
73
x = norm.ppf(.05, mean, std_dev)
print(x) # 15.53271955957279
Por lo tanto, si logramos un promedio de 15,53 días o menos de tiempo de recuperación en nuestro
grupo de muestra, nuestro medicamento se considera estadísticamente lo suficientemente
significativo como para haber mostrado un impacto. Sin embargo, nuestra media muestral de
tiempo de recuperación es en realidad 16 días y no cae en esta zona de rechazo de hipótesis nula.
Por lo tanto, la prueba de significancia estadística ha fallado como se muestra en la Figura 3-19.
Figura 3-19. No hemos podido probar que el resultado de nuestra prueba de drogas sea
estadísticamente significativo.
El área hasta esa marca de 16 días es nuestro valor p, que es.0912, y lo calculamos en Python
como se muestra en el Ejemplo 3-20.
Ejemplo 3-20. Cálculo del valor p de una cola
from scipy.stats import norm
# Cold has 18 day mean recovery, 1.5 std dev
mean = 18
std_dev = 1.5
# Probability of 16 or less days
p_value = norm.cdf(16, mean, std_dev)
print(p_value) # 0.09121121972586788
Dado que el valor p de 0,0912 es mayor que nuestro umbral de significancia estadística de 0,05, no
consideramos que el ensayo del fármaco haya sido un éxito y no rechazamos nuestra hipótesis
nula.
H1 : population mean ≠ 18
Esto tiene una implicación importante. Estamos estructurando nuestra hipótesis alternativa para no
probar si el fármaco mejora el tiempo de recuperación del frío, pero si tuvo algún impacto. Esto
incluye probar si aumentó la duración del resfriado. ¿Es esto útil? Sostenga ese pensamiento.
Naturalmente, esto significa que extendemos nuestro umbral de significancia estadística del valor p
a ambas colas, no solo a una. Si estamos probando una significación estadística del 5 %, entonces
la dividimos y le damos a cada 2,5 % la mitad de cada cruz. Si el tiempo medio de recuperación de
74
nuestro fármaco cae en cualquier región, nuestra prueba tiene éxito y rechazamos la hipótesis nula
( Figura 3-20 ).
Los valores de x para la cola inferior y la cola superior son 15,06 y 20,93, lo que significa que si
estamos por debajo o por encima, respectivamente, rechazamos la hipótesis nula. Esos dos
valores se calculan utilizando la CDF inversa que se muestra en la Figura 3-21 y el Ejemplo 3-21.
Recuerde, para obtener la cola superior, tomamos.95 y luego le agregamos el umbral de
significancia de.025, lo que nos da.975.
Figura 3-21. Cálculo del 95% central del área de distribución normal
El valor medio de la muestra para el grupo de prueba de drogas es 16, y 16 no es inferior a 15,06 ni
superior a 20,9399. Entonces, al igual que la prueba de una cola, aún no podemos rechazar la
75
hipótesis nula. Nuestro fármaco aún no ha mostrado ninguna significación estadística para tener
algún impacto, como se muestra en la Figura 3-22.
Pero, ¿cuál es el valor p? Aquí es donde se pone interesante con las pruebas de dos colas.
Nuestro valor p va a capturar no solo el área a la izquierda de 16, sino también el área equivalente
simétrica en la cola derecha. Dado que 16 está 4 días por debajo de la media, también
capturaremos el área por encima de 20, que está 4 días por encima de la media ( Figura 3-23 ).
Esto captura la probabilidad de un evento o más raro, en ambos lados de la curva de campana.
Cuando sumamos ambas áreas, obtenemos un valor p de.1824. Esto es mucho mayor que.05, por
lo que definitivamente no pasa nuestro umbral de valor p de.05 ( Ejemplo 3-22 ).
Ejemplo 3-22. Cálculo del valor p de dos colas
from scipy.stats import norm
# Cold has 18 day mean recovery, 1.5 std dev
mean = 18
std_dev = 1.5
# Probability of 16 or less days
p1 = norm.cdf(16, mean, std_dev)
# Probability of 20 or more days
p2 = 1.0 - norm.cdf(20, mean, std_dev)
# P-value of both tails
p_value = p1 + p2
print(p_value) # 0.18242243945173575
Entonces, ¿por qué también agregamos el área simétrica en el lado opuesto en una prueba de dos
colas? Puede que este no sea el concepto más intuitivo, pero primero recuerda cómo
estructuramos nuestras hipótesis:
76
H : population mean = 18
0
H1 : population mean ≠ 18
Si estamos probando en una capacidad de "igual a 18" versus "no igual a 18", tenemos que
capturar cualquier probabilidad que sea de igual o menor valor en ambos lados. Después de todo,
estamos tratando de probar la importancia, y eso incluye cualquier cosa que tenga las mismas o
menos probabilidades de suceder. No tuvimos esta consideración especial con la prueba de una
cola que usaba solo la lógica "mayor/menor que". Pero cuando tratamos con “iguales/no iguales”
nuestra área de interés va en ambas direcciones.
Entonces, ¿cuáles son las implicaciones prácticas de la prueba de dos colas? ¿Cómo afecta si
rechazamos la hipótesis nula? Pregúntese esto: ¿cuál establece un umbral más alto? Notará que
incluso cuando nuestro objetivo es mostrar que es posible que hayamos disminuido algo (el tiempo
de recuperación del resfriado usando un fármaco), reformular nuestra hipótesis para mostrar
cualquier impacto (mayor o menor) crea un umbral de significación más alto. Si nuestro umbral de
significación es un valor de p de 0,05 o menos, nuestra prueba de una cola estuvo más cerca de la
aceptación con un valor de p de 0,0912 en comparación con la prueba de dos colas, que fue
aproximadamente el doble con un valor de p de 0,182.
Esto significa que la prueba de dos colas hace que sea más difícil rechazar la hipótesis nula y exige
pruebas más sólidas para pasar la prueba. Piensa también en esto: ¿y si nuestro medicamento
pudiera empeorar los resfriados y hacerlos durar más? También puede ser útil capturar esa
probabilidad y tener en cuenta la variación en esa dirección. Esta es la razón por la cual las
pruebas de dos colas son preferibles en la mayoría de los casos. Tienden a ser más confiables y no
sesgan la hipótesis en una sola dirección.
Usaremos pruebas de hipótesis y valores p nuevamente en los capítulos 5 y 6.
77
incertidumbre. La figura 3-24 muestra una distribución normal (discontinua) junto con una
distribución T con un grado de libertad (continua).
Figura 3-24. La distribución T junto con una distribución normal; nota las colas más gordas
Cuanto menor sea el tamaño de la muestra, más gruesas serán las colas en una distribución T.
Pero lo interesante es que después de acercarse a 31 elementos, la distribución T es casi
indistinguible de la distribución normal, lo que refleja claramente las ideas detrás del teorema del
límite central.
El ejemplo 3-23 muestra cómo encontrar el valor t crítico para una confianza del 95 %. Puede usar
esto para intervalos de confianza y pruebas de hipótesis cuando tiene un tamaño de muestra de 30
o menos. Es conceptualmente lo mismo que el valor z crítico, pero estamos usando una
distribución T en lugar de una distribución normal para reflejar una mayor incertidumbre. Cuanto
menor sea el tamaño de la muestra, mayor será el rango, lo que refleja una mayor incertidumbre.
Ejemplo 3-23. Obtener un rango de valores críticos con una distribución T
from scipy.stats import t
# get critical value range for 95% confidence
# with a sample size of 25
n = 25
lower = t.ppf(.025, df=n-1)
upper = t.ppf(.975, df=n-1)
print(lower, upper)
-2.063898561628021 2.0638985616280205
78
Consideraciones sobre Big Data y la falacia del francotirador de Texas
Una reflexión final antes de cerrar este capítulo. Como hemos discutido, la aleatoriedad
juega un papel tan importante en la validación de nuestros hallazgos y siempre tenemos
que dar cuenta de su posibilidad. Desafortunadamente, con el big data, el aprendizaje
automático y otras herramientas de minería de datos, el método científico se ha convertido
repentinamente en una práctica hecha al revés. Esto puede ser precario; permítanme
demostrar por qué, adaptando un ejemplo del libro Standard Deviations de Gary Smith
(Overlook Press).
Supongamos que saco cuatro naipes de una baraja justa. Aquí no hay ningún juego u
objetivo más que sacar cuatro cartas y observarlas. Obtengo dos 10, un 3 y un 2. “Esto es
interesante”, digo. “Obtuve dos 10, un 3 y un 2. ¿Es esto significativo? ¿Las próximas
cuatro cartas que saque también serán dos números consecutivos y un par? ¿Cuál es el
modelo subyacente aquí?
¿Ves lo que hice ahí? Tomé algo que era completamente aleatorio y no solo busqué
patrones, sino que traté de hacer un modelo predictivo a partir de ellos. Lo que sucedió
sutilmente aquí es que nunca me propuse obtener estas cuatro cartas con estos patrones
particulares. Los observé después de que ocurrieron.
Esto es exactamente de lo que la minería de datos es víctima todos los días: encontrar
patrones coincidentes en eventos aleatorios. Con enormes cantidades de datos y
algoritmos rápidos que buscan patrones, es fácil encontrar cosas que parecen
significativas pero que en realidad son solo coincidencias aleatorias.
Esto también es análogo a mí disparando un arma a una pared. Luego dibujo un objetivo
alrededor del agujero y traigo a mis amigos para mostrarles mi increíble puntería. tonto,
cierto? Bueno, muchas personas en ciencia de datos hacen esto figurativamente todos los
días y se conoce como la falacia del francotirador de Texas. Se dispusieron a actuar sin un
objetivo, tropezaron con algo raro y luego señalaron que lo que encontraron de alguna
manera crea valor predictivo.
El problema es que la ley de los números realmente grandes dice que es probable que se
encuenTrain eventos raros; simplemente no sabemos cuáles. Cuando nos encontramos
con eventos raros, destacamos e incluso especulamos sobre qué podría haberlos causado.
El problema es este: la probabilidad de que una persona específica gane la lotería es muy
poco probable, pero aún así alguien ganará la lotería. ¿Por qué deberíamos sorprendernos
cuando hay un ganador? A menos que alguien predijera el ganador, no sucedió nada
significativo aparte de que una persona al azar tuvo suerte.
Esto también se aplica a las correlaciones, que estudiaremos en el Capítulo 5. Con un
enorme conjunto de datos con miles de variables, ¿es fácil encontrar resultados
estadísticamente significativos con un valor p de 0,05? ¡Puedes apostar! Encontraré miles
de esos. Incluso mostraré evidencia de que la cantidad de películas de Nicolas Cage se
correlaciona con la cantidad de ahogamientos en piscinas en un año.
Entonces, para evitar la falacia del francotirador de Texas y ser víctima de falacias de big
data, intente usar pruebas de hipótesis estructuradas y recopile datos para ese objetivo. Si
utiliza la minería de datos, intente obtener datos nuevos para ver si sus hallazgos aún se
mantienen. Finalmente, considere siempre la posibilidad de que las cosas puedan ser
coincidentes; si no hay una explicación de sentido común, entonces probablemente fue
una coincidencia.
Aprendimos a formular hipótesis antes de recopilar datos, pero la minería de datos recopila
datos y luego formula hipótesis. Irónicamente, a menudo somos más objetivos al comenzar
con una hipótesis, porque luego buscamos datos para probar y refutar deliberadamente
nuestra hipótesis.
Conclusión
Aprendimos mucho en este capítulo, y deberías sentirte bien por haber llegado tan lejos.
¡Este fue probablemente uno de los temas más difíciles de este libro! No solo aprendimos
79
estadísticas descriptivas desde la media hasta la distribución normal, sino que también
abordamos los intervalos de confianza y las pruebas de hipótesis.
Con suerte, usted ve los datos un poco diferente. Son instantáneas de algo más que una
captura completa de la realidad. Los datos por sí solos no son muy útiles, y necesitamos
contexto, curiosidad y análisis de su origen antes de que podamos obtener información
significativa con ellos. Cubrimos cómo describir datos e inferir atributos sobre una
población más grande en función de una muestra. Finalmente, abordamos algunas de las
falacias de minería de datos con las que podemos tropezar si no tenemos cuidado, y cómo
remediarlo con datos nuevos y sentido común.
No se sienta mal si necesita volver atrás y revisar parte del contenido de este capítulo,
porque hay mucho que digerir. También es importante tener una mentalidad de prueba de
hipótesis si desea tener éxito en una carrera de ciencia de datos y aprendizaje automático.
Pocos profesionales se toman el tiempo para vincular las estadísticas y los conceptos de
prueba de hipótesis con el aprendizaje automático, y eso es desafortunado.
La comprensibilidad y la explicabilidad son la próxima frontera del aprendizaje automático,
así que continúe aprendiendo e integrando estas ideas a medida que avanza en el resto de
este libro y el resto de su carrera.
Ejercicios
1. Has comprado una bobina de filamento de 1,75 mm para tu impresora 3D. Desea
medir qué tan cerca está realmente el diámetro del filamento de 1,75 mm. Utiliza
una herramienta de calibre y toma muestras del diámetro cinco veces en el carrete:
1.78, 1.75, 1.72, 1.74, 1.77
Calcule la media y la desviación estándar para este conjunto de valores.
2. Un fabricante dice que el teléfono inteligente Z-Phone tiene una vida media de
consumo de 42 meses con una desviación estándar de 8 meses. Suponiendo una
distribución normal, ¿cuál es la probabilidad de que un Z-Phone aleatorio dado dure
entre 20 y 30 meses?
3. Dudo que el filamento de mi impresora 3D no tenga un diámetro medio de 1,75
mm como se anuncia. Tomé muestras de 34 medidas con mi herramienta. La media
muestral es 1,715588 y la desviación estándar muestral es 0,029252.
¿Cuál es el intervalo de confianza del 99 % para la media de toda mi bobina de
filamento?
4. Su departamento de marketing ha iniciado una nueva campaña publicitaria y
quiere saber si afectó las ventas, que en el pasado promediaron $10 345 por día con
una desviación estándar de $552. La nueva campaña publicitaria duró 45 días y
promedió $11,641 en ventas.
¿La campaña afectó las ventas? ¿Por qué o por qué no? (Use una prueba de dos colas
para una significación más confiable).
80
Capítulo 4. Álgebra lineal
Cambiando un poco de tema, aventurémonos de la probabilidad y la estadística y adentrémonos en
el álgebra lineal. A veces la gente confunde el álgebra lineal con el álgebra básica, pensando que
tal vez tiene que ver con trazar líneas usando la función algebraica y = mx + b. Esta es la razón por
la cual el álgebra lineal probablemente debería haberse llamado "álgebra vectorial" o "álgebra
matricial" porque es mucho más abstracta. Los sistemas lineales juegan un papel pero de una
manera mucho más metafísica.
Entonces, ¿qué es exactamente el álgebra lineal? Bueno, el álgebra lineal se ocupa de los
sistemas lineales pero los representa a través de espacios vectoriales y matrices. Si no sabes qué
es un vector o una matriz, ¡no te preocupes! Los definiremos y exploraremos en profundidad. El
álgebra lineal es muy fundamental para muchas áreas aplicadas de las matemáticas, la estadística,
la investigación de operaciones, la ciencia de datos y el aprendizaje automático. Cuando trabaja
con datos en cualquiera de estas áreas, está utilizando álgebra lineal y tal vez ni siquiera lo sepa.
Puede salirse con la suya sin aprender álgebra lineal por un tiempo, utilizando bibliotecas de
estadísticas y aprendizaje automático que lo hacen todo por usted. Pero si va a obtener intuición
detrás de estas cajas negras y ser más efectivo al trabajar con datos, es inevitable comprender los
fundamentos del álgebra lineal. El álgebra lineal es un tema enorme que puede llenar libros de
texto gruesos, por lo que, por supuesto, no podemos obtener un dominio total en solo un capítulo
de este libro. Sin embargo, podemos aprender lo suficiente para sentirnos más cómodos con él y
navegar el dominio de la ciencia de datos de manera efectiva. También habrá oportunidades para
aplicarlo en los capítulos restantes de este libro, incluidos los capítulos 5 y 7.
¿Qué es un vector?
En pocas palabras, un vector es una flecha en el espacio con una dirección y longitud específicas,
que a menudo representa un dato. Es el componente central del álgebra lineal, incluidas las
matrices y las transformaciones lineales. En su forma fundamental, no tiene concepto de ubicación,
así que siempre imagine que su cola comienza en el origen de un plano cartesiano (0,0).
La figura 4-1 muestra un vector que se mueve tres pasos en dirección horizontal y dos pasos en
dirección vertical.
Para enfatizar nuevamente, el propósito del vector es representar visualmente una pieza de datos.
Si tiene un registro de datos de los pies cuadrados de una casa de 18 000 pies cuadrados y su
valoración de $260 000, podríamos expresarlo como un vector [18 000, 2600 000], con 18 000
pasos en dirección horizontal y 260 000 pasos en dirección vertical.
Declaramos un vector matemáticamente así:
81
Podemos declarar un vector usando una colección de Python simple, como una lista de Python
como se muestra en el Ejemplo 4-1.
Ejemplo 4-1. Declarar un vector en Python usando una lista
v = [3, 2]
print(v)
Sin embargo, cuando empezamos a hacer cálculos matemáticos con vectores, especialmente
cuando hacemos tareas como el aprendizaje automático, probablemente deberíamos usar la
biblioteca NumPy, ya que es más eficiente que Python simple. También puede usar SymPy para
realizar operaciones de álgebra lineal, y lo usaremos ocasionalmente en este capítulo cuando los
decimales se vuelvan inconvenientes. Sin embargo, NumPy es lo que probablemente usará en la
práctica, por lo que nos apegaremos principalmente.
Para declarar un vector, puede usar la función array() de NumPy y luego puede pasarle una
colección de números como se muestra en el Ejemplo 4-2.
Ejemplo 4-2. Declarar un vector en Python usando NumPy
import numpy as np
v = np.array([3, 2])
print(v)
82
Figura 4-2. Una muestra de diferentes vectores.
¿POR QUÉ SON ÚTILES LOS VECTORES?
Una lucha que muchas personas tienen con los vectores (y el álgebra lineal en general) es
entender por qué son útiles. Son un concepto muy abstracto pero tienen muchas aplicaciones
tangibles. Es más fácil trabajar con los gráficos por computadora cuando se dominan los vectores y
las transformaciones lineales (la fantástica biblioteca de visualización de Manim define animaciones
y transformaciones con vectores). Cuando se realizan trabajos de estadísticas y aprendizaje
automático, los datos a menudo se importan y se convierten en vectores numéricos para que
podamos trabajar con ellos. Los solucionadores como el de Excel o Python PuLP usan
programación lineal, que usa vectores para maximizar una solución mientras cumple con esas
restricciones. Incluso los videojuegos y los simuladores de vuelo usan vectores y álgebra lineal
para modelar no solo gráficos sino también física. Creo que lo que hace que los vectores sean tan
difíciles de comprender no es que su aplicación no sea obvia, sino que estas aplicaciones son tan
diversas que es difícil ver la generalización.
Tenga en cuenta que también los vectores pueden existir en más de dos dimensiones. A
continuación, declaramos un vector tridimensional a lo largo de los ejes x, y, y z:
Para crear este vector, estamos dando cuatro pasos en la dirección x, uno en la dirección y y dos
en la dirección z. Aquí se visualiza en la Figura 4-3. Tenga en cuenta que ya no estamos
mostrando un vector en una cuadrícula bidimensional, sino un espacio tridimensional con tres ejes:
x, y y z.
Naturalmente, podemos expresar este vector tridimensional en Python usando tres valores
numéricos, como se declara en el Ejemplo 4-3.
Ejemplo 4-3. Declarar un vector tridimensional en Python usando NumPy
import numpy as np
v = np.array([4, 1, 2])
print(v)
Como muchos modelos matemáticos, visualizar más de tres dimensiones es un desafío y es algo
en lo que no gastaremos energía en este libro. Pero numéricamente, sigue siendo sencillo. El
ejemplo 4-4 muestra cómo declaramos matemáticamente un vector de cinco dimensiones en
Python.
83
import numpy as np
v = np.array([6, 1, 5, 8, 3])
print(v)
Adición y combinación de vectores
Por sí solos, los vectores no son terriblemente interesantes. Expresas una dirección y un tamaño,
algo así como un movimiento en el espacio. Pero cuando empiezas a combinar vectores, lo que se
conoce como suma de vectores, empieza a ponerse interesante. Combinamos efectivamente los
movimientos de dos vectores en un solo vector.
Digamos que tenemos dos vectores como se muestra en la figura 4-4. ¿Cómo sumamos
estos dos vectores?
Veremos por qué es útil agregar vectores en un momento. Pero si quisiéramos combinar estos dos
vectores, incluida su dirección y escala, ¿cómo sería eso? Numéricamente, esto es sencillo.
Simplemente agregue los valores x respectivos y luego los valores y en un nuevo vector como se
muestra en el ejemplo 4-5.
84
Pero, ¿qué significa esto visualmente? Para sumar visualmente estos dos vectores, conecte un
vector tras otro y camine hasta la punta del último vector ( Figura 4-5 ). El punto en el que terminas
es un nuevo vector, el resultado de sumar los dos vectores.
Como se ve en la Figura 4-5, cuando caminamos hasta el final del último vector terminamos
con un nuevo vector [5, 1]. Este nuevo vector es el resultado de sumar En la práctica, esto
puede consistir simplemente en sumar datos. Si tuviéramos que sumar los valores de las viviendas
y sus pies cuadrados en un área, estaríamos sumando varios vectores en un solo vector de esta
manera.
Tenga en cuenta que no importa si agregamos antes de o viceversa, lo que significa que es
conmutativo y el orden de operación no importa. Si caminamos antes de terminamos con el
mismo vector resultante [5, 1] como se visualiza en la figura 4-6.
Vectores de escala
85
Escalar es aumentar o reducir la longitud de un vector. Puede hacer crecer/reducir un vector
multiplicándolo o escalando con un solo valor, conocido como escalar. La figura 4-7 es el siendo
escalado por un factor de 2, lo que lo duplica.
Realizando esta operación de escalado en Pythones tan fácil como multiplicar un vector por el
escalar, como se codifica en el Ejemplo 4-6.
Ejemplo 4-6. Escalar un número en Python usando NumPy
from numpy import array
v = array([3,1])
# scale the vector
scaled_v = 2.0 * v
# display scaled vector
print(scaled_v) # [6 2]
Aquí en la Figura 4-8 v → está siendo reducido por un factor de.5, lo que lo reduce a la mitad.
86
MANIPULAR DATOS ES MANIPULAR VECTORES
Cada operación de datos se puede pensar en términos de vectores, incluso promedios simples.
Tome la escala, por ejemplo. Digamos que estamos tratando de obtener el valor promedio de la
casa y el promedio de pies cuadrados para todo un vecindario. Agregaríamos los vectores para
combinar su valor y pies cuadrados respectivamente, dándonos un vector gigante que contiene
tanto el valor total como el total de pies cuadrados. Luego, reducimos la escala del vector
dividiendo por el número de casas N, que en realidad se multiplica por 1/ N. Ahora tenemos un
vector que contiene el valor promedio de la casa y el promedio de pies cuadrados.
Un detalle importante a tener en cuenta aquí es que escalar un vector no cambia su dirección, solo
su magnitud. Pero hay una pequeña excepción a esta regla, como se muestra en la figura 4-9.
Cuando multiplicas un vector por un número negativo, cambia la dirección del vector como se
muestra en la imagen.
Sin embargo, cuando lo piensa, la escala por un número negativo realmente no ha cambiado de
dirección en el sentido de que todavía existe en la misma línea. Esto da paso a un concepto clave
llamado dependencia lineal.
87
Figura 4-10. Escalar dos vectores agregados nos permite crear cualquier vector nuevo
Una vez más, tienen una dirección fija, excepto para voltear con escalares negativos, pero
podemos usar la escala para crear libremente cualquier vector compuesto por .
Todo este espacio de posibles vectores se llama span y, en la mayoría de los casos, nuestro span
puede crear vectores ilimitados a partir de esos dos vectores, simplemente al escalarlos y
sumarlos. Cuando tenemos dos vectores en dos direcciones diferentes, son linealmente
independientes y tienen este lapso ilimitado.
Pero, ¿en qué caso estamos limitados en los vectores que podemos crear? Piénsalo y sigue
leyendo.
¿Qué sucede cuando existen dos vectores en la misma dirección, o existen en la misma línea? La
combinación de esos vectores también está atascada en la misma línea, lo que limita nuestro lapso
solo a esa línea. No importa cómo lo escale, el vector de suma resultante también está atascado en
esa misma línea. Esto los hace linealmente dependientes, como se muestra en la Figura 4-11.
El lapso aquí está atascado en la misma línea que los dos vectores de los que está hecho. Debido
a que los dos vectores existen en la misma línea subyacente, no podemos crear de manera flexible
ningún vector nuevo a través de la escala.
88
En tres o más dimensiones, cuando tenemos un conjunto de vectores linealmente dependientes, a
menudo nos quedamos atascados en un plano en un número menor de dimensiones. Este es un
ejemplo de estar atascado en un plano bidimensional aunque tengamos vectores tridimensionales
como se declara en la figura 4-12.
Figura 4-12. Dependencia lineal en tres dimensiones; tenga en cuenta que nuestro lapso está
limitado a un plano plano
Más adelante aprenderemos una herramienta simple llamada determinante para verificar la
dependencia lineal, pero ¿por qué nos importa si dos vectores son linealmente dependientes o
independientes?? Muchos problemas se vuelven difíciles o irresolubles cuando son linealmente
dependientes. Por ejemplo, cuando aprendamos acerca de los sistemas de ecuaciones más
adelante en este capítulo, un conjunto de ecuaciones linealmente dependientes puede hacer que
las variables desaparezcan y que el problema sea imposible de resolver. Pero si tiene
independencia lineal, esa flexibilidad para crear cualquier vector que necesite a partir de dos o más
vectores se vuelve invaluable para encontrar una solución.!
Transformaciones lineales
Este concepto de sumar dos vectores con dirección fija, pero escalarlos para obtener diferentes
vectores combinados, es muy importante. Este vector combinado, salvo en los casos de
dependencia lineal, puede apuntar en cualquier dirección y tener la longitud que elijamos. Esto
establece una intuición para las transformaciones lineales donde usamos un vector para
transformar otro vector de manera similar a una función.
Vectores base
Imagina que tenemos dos vectores simples ("i-sombrero" y "j-sombrero"). Estos se conocen
como vectores base, que se utilizan para describir transformaciones en otros vectores. Por lo
general, tienen una longitud de 1 y apuntan en direcciones perpendiculares positivas, como se
muestra en la Figura 4-13.
89
Piense en los vectores base como bloques de construcción para construir o transformar cualquier
vector. Nuestro vector base se expresa en una matriz de 2 × 2, donde la primera columna es y la
segunda columna es :
Una matriz es una colección de vectores (como ) que pueden tener múltiples filas y columnas
y es una forma conveniente de empaquetar datos. Podemos usar para crear cualquier
vector que queramos al escalarlos y agregarlos. Comencemos con cada uno con una longitud de 1
y mostrando el vector resultante en la Figura 4-14.
Quiero que el vector aterrice en [3, 2]. ¿Qué sucede con si estiramos por un factor de 3
y por un factor de 2? Primero los escalamos individualmente como se muestra aquí:
Si estiramos el espacio en estas dos direcciones, ¿qué le hace esto a ? Bueno, se va a estirar
con . Esto se conoce como transformación lineal, en la que transformamos un vector con
estiramiento, aplastamiento, corte o rotación mediante el seguimiento de los movimientos del vector
base. En este caso ( Figura 4-15 ), escalar ha estirado el espacio junto con nuestro vector
90
Figura 4-15. Una transformación lineal
Pero, ¿dónde aterriza ? Es fácil ver dónde aterriza aquí, que es [3, 2]. Recuerda que el vector
se compone de sumar . Así que simplemente tomamos . estirados y los sumamos
para ver dónde ha aterrizado el vector :
91
Estas cuatro transformaciones lineales son una parte central del álgebra lineal. Escalar un vector lo
estirará o apretará. Las rotaciones cambiarán el espacio vectorial y las inversiones cambiarán el
espacio vectorial para que intercambien sus respectivos lugares.
Es importante tener en cuenta que no puede tener transformaciones que no sean lineales, lo que
da como resultado transformaciones con curvas o onduladas que ya no respetan una línea recta.
Es por eso que lo llamamos álgebra lineal, no álgebra no lineal.!
92
queremos, completando cada vector como una fila en lugar de una columna. La transposición en
NumPy se demuestra en el Ejemplo 4-8.
Ejemplo 4-8. Separando los vectores base y aplicándolos como una transformación
from numpy import array
# Declare i-hat and j-hat
i_hat = array([2, 0])
j_hat = array([0, 3])
# compose basis matrix using i-hat and j-hat
# also need to transpose rows into columns
basis = array([i_hat, j_hat]).transpose()
# declare vector v
v = array([1,1])
# create new vector
# by transforming v with dot product
new_v = basis.dot(v)
print(new_v) # [2, 3]
Aquí hay otro ejemplo. Comencemos con el vector siendo [2, 1] y y comienzan en [1, 0] y
[0, 1], respectivamente. Luego transformamos y en [2, 0] y [0, 3]. ¿Qué sucede con el vector
? Trabajando esto matemáticamente a mano usando nuestra fórmula, obtenemos esto:
El vector ahora aterriza en [4, 3]. La figura 4-17 muestra cómo se ve esta transformación.
93
Figura 4-17. Una transformación lineal de estiramiento
Aquí hay un ejemplo que salta las cosas un poco. Tomemos el vector de valor [2, 1]. y
comienzan en [1, 0] y [0, 1], pero luego se transforman y terminan en [2, 3] y [2, -1]. ¿Qué sucede
con ? Veamos la Figura 4-18 y el Ejemplo 4-10.
Figura 4-18. Una transformación lineal que hace una rotación, corte y volteo del espacio.
94
cambian de lugar en su orientación en el sentido de las agujas del reloj, y aprenderemos cómo
detectar esto con determinantes más adelante en este capítulo.
Otra cosa que vale la pena señalar es que algunas transformaciones lineales pueden cambiar un
espacio vectorial a menos o más dimensiones, y eso es exactamente lo que harán las matrices no
cuadradas (donde el número de filas y columnas no es igual). En interés del tiempo no podemos
explorar este. Pero 3Blue1Brown explica y anima este concepto maravillosamente.
Multiplicación de matrices
Aprendimos a multiplicar un vector y una matriz, pero ¿qué se logra exactamente al multiplicar dos
matrices? Piense en la multiplicación de matrices como la aplicación de múltiples transformaciones
a un espacio vectorial. Cada transformación es como una función, donde primero aplicamos la más
interna y luego aplicamos cada transformación posterior hacia afuera.
Así es como aplicamos una rotación y luego un corte a cualquier vector con valor [ x, y ]:
De hecho, podemos consolidar estas dos transformaciones usando esta fórmula, aplicando una
transformación sobre la última. Multiplicas y sumas cada fila de la primera matriz a cada columna
respectiva de la segunda matriz, en un "arriba y abajo". arriba y abajo!” patrón:
Entonces podemos consolidar estas dos transformaciones separadas (rotación y corte) en una sola
transformación:
95
Para ejecutar esto en Python usando NumPy, puede combinar las dos matrices simplemente
usando el operador matmul() o @ ( Ejemplo 4-11 ). Luego daremos la vuelta y usaremos esta
transformación consolidada y la aplicaremos a un vector [1, 2].
96
Piense en cada transformación como una función, y las aplicamos de la más interna a la más
externa, como llamadas a funciones anidadas.
Determinantes
Cuando realizamos transformaciones lineales, a veces "expandimos" o "aplastamos" el espacio y el
grado en que esto sucede puede ser útil. Tome un área muestreada del espacio vectorial de la
figura 4-20 : ¿qué le sucede después de escalar y ?
Figura 4-20. Un determinante mide cómo una transformación lineal escala un área
Tenga en cuenta que aumenta en área por un factor de 6.0, y este factor se conoce como
determinante. Los determinantes describen cuánto cambia en escala un área muestreada en un
espacio vectorial con transformaciones lineales, y esto puede proporcionar información útil sobre la
transformación.
El ejemplo 4-13 muestra cómo calcular este determinante en Python.
Ejemplo 4-13. Cálculo de un determinante
from numpy.linalg import det
from numpy import array
i_hat = array([3, 0])
j_hat = array([0, 2])
basis = array([i_hat, j_hat]).transpose()
determinant = det(basis)
print(determinant) # prints 6.0
Los cortes y rotaciones simples no deberían afectar el determinante, ya que el área no cambiará.
La figura 4-21 y el ejemplo 4-14 muestran un cortante simple y el determinante sigue siendo un
factor 1.0, lo que muestra que no cambia.
97
Figura 4-21. Un simple cortante no cambia el determinante.
98
j_hat = array([1, 2])
basis = array([i_hat, j_hat]).transpose()
determinant = det(basis)
print(determinant) # prints -5.0
Debido a que este determinante es negativo, vemos rápidamente que la orientación se ha invertido.
Pero, con mucho, la información más crítica que te dice el determinante es si la transformación es
linealmente dependiente. Si tiene un determinante de 0, eso significa que todo el espacio se ha
aplastado en una dimensión menor.
En la Figura 4-23 vemos dos transformaciones linealmente dependientes, donde un espacio 2D se
comprime en una dimensión y un espacio 3D se comprime en dos dimensiones. ¡El área y el
volumen respectivamente en ambos casos son 0!
Matriz cuadrada
La matriz cuadrada es una matriz que tiene igual número de filas y columnas:
Matriz de identidad
99
La matriz identidad es una matriz cuadrada que tiene una diagonal de 1s mientras que los otros
valores son 0:
¿Cuál es el problema con las matrices de identidad? Bueno, cuando tienes una matriz de identidad,
esencialmente has deshecho una transformación y encontrado tus vectores base iniciales. Esto
jugará un papel importante en la resolución de sistemas de ecuaciones en la siguiente sección.
Matriz inversa
Una matriz inversa es una matriz que deshace la transformación de otra matriz. Digamos que tengo
la matriz A :
Matriz diagonal
Similar a la matriz identidad es la matriz diagonal, que tiene una diagonal de valores distintos de
cero mientras que el resto de los valores son 0. Las matrices diagonales son deseables en ciertos
cálculos porque representan escalares simples que se aplican a un espacio vectorial. Aparece en
algunas operaciones de álgebra lineal.
matriz triangular
Similar a la matriz diagonal es la matriz triangular, que tiene una diagonal de valores distintos de
cero delante de un triángulo de valores, mientras que el resto de los valores son 0.
Las matrices triangulares son deseables en muchas tareas de análisis numérico porque, por lo
general, son más fáciles de resolver en sistemas de ecuaciones. También aparecen en ciertas
tareas de descomposición como LU Decomposition.
100
Matriz dispersa
Ocasionalmente, se encontrará con matrices que en su mayoría son ceros y tienen muy pocos
elementos distintos de cero. Estas se llaman matrices dispersas. Desde un punto de vista
matemático puro, no son terriblemente interesantes. Pero desde el punto de vista informático,
brindan oportunidades para crear eficiencia. Si una matriz tiene principalmente 0, una
implementación de matriz dispersa no desperdiciará espacio almacenando un montón de 0 y, en
cambio, solo realizará un seguimiento de las celdas que no sean cero.
Cuando tiene matrices grandes que son escasas, puede usar explícitamente una función escasa
para crear su matriz.
Necesitamos "deshacer" A para poder aislar X y obtener los valores para x, y y z. La forma de
deshacer A es tomar el inverso de A denotado por A-1 y aplicarlo a A a través de la multiplicación de
matrices. Podemos expresar esto algebraicamente:
AX = B
A-1AX = A-1B
X = A-1B
101
Tenga en cuenta que cuando multiplicamos la matriz A -1 contra A, se creará una matriz de
identidad, una matriz de todos los ceros excepto los 1 en la diagonal. La matriz de identidad es el
álgebra lineal equivalente a multiplicar por 1, lo que significa que esencialmente no tiene ningún
efecto y aislará de manera efectiva los valores de x, y y z :
Para ver esta matriz de identidad en acción en Python, querrá usar SymPy en lugar de NumPy. Los
decimales de punto flotante en NumPy no harán que la matriz de identidad sea tan obvia, pero al
hacerlo simbólicamente en el Ejemplo 4-17, veremos una salida simbólica limpia.Tenga en cuenta
que para hacer la multiplicación de matrices en SymPy usamos el asterisco * en lugar de @.
Ejemplo 4-17. Usando SymPy para estudiar la matriz inversa e identidad
from sympy import *
# 4x + 2y + 4z = 44
# 5x + 3y + 7z = 56
# 9x + 3y + 6z = 72
A = Matrix([
[4, 2, 4],
[5, 3, 7],
[9, 3, 6]
])
# dot product between A and its inverse
# will produce identity function
inverse = A.inv()
identity = inverse * A
# prints Matrix([[-1/2, 0, 1/3], [11/2, -2, -4/3], [-2, 1, 1/3]])
print("INVERSE: {}".format(inverse))
# prints Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
print("IDENTITY: {}".format(identity))
102
from numpy import array
from numpy.linalg import inv
# 4x + 2y + 4z = 44
# 5x + 3y + 7z = 56
# 9x + 3y + 6z = 72
A = array([
[4, 2, 4],
[5, 3, 7],
[9, 3, 6]
])
B = array([
44,
56,
72
])
X = inv(A).dot(B)
print(X) # [ 2. 34. -8.]
Con suerte, esto le dio una intuición para las matrices inversas y cómo se pueden usar para
resolver un sistema de ecuaciones.
103
PatrickJMT tiene muchos buenos videos sobre programación lineal. También lo cubrimos
brevemente en el Apéndice A.
En la práctica, rara vez encontrará necesario calcular matrices inversas a mano y puede hacer que
una computadora lo haga por usted. Pero si tiene una necesidad o tiene curiosidad, querrá
aprender sobre la eliminación gaussiana. PatrickJMT en YouTube tiene una serie de videos que
demuestran la eliminación de Gauss.
104
"""
EIGENVALUES
[-0.46410162 6.46410162]
EIGENVECTORS
[[-0.80689822 -0.34372377]
[ 0.59069049 -0.9390708 ]]
"""
Entonces, ¿cómo reconstruimos la matriz A a partir de los vectores propios y los valores propios?
Recuerda esta fórmula:
Av = λv
Necesitamos hacer algunos ajustes a la fórmula para reconstruir A :
A = QΛQ-1
En esta nueva fórmula, Q son los vectores propios, Λ son los valores propios en forma diagonal y
Q -1 es la matriz inversa de Q. La forma diagonal significa que el vector se rellena en una matriz de
ceros y ocupa la línea diagonal en un patrón similar a una matriz de identidad.
El ejemplo 4-21 completa el círculo del ejemplo en Python, comenzando con la descomposición de
la matriz y luego recomponiéndola.
Ejemplo 4-21. Descomponer y recomponer una matriz en NumPy
from numpy import array, diag
from numpy.linalg import eig, inv
A = array([
[1, 2],
[4, 5]
])
eigenvals, eigenvecs = eig(A)
print("EIGENVALUES")
print(eigenvals)
print("\nEIGENVECTORS")
print(eigenvecs)
print("\nREBUILD MATRIX")
Q = eigenvecs
R = inv(Q)
L = diag(eigenvals)
B=Q@L@R
print(B)
"""
EIGENVALUES
[-0.46410162 6.46410162]
EIGENVECTORS
[[-0.80689822 -0.34372377]
[ 0.59069049 -0.9390708 ]]
REBUILD MATRIX
[[1. 2.]
[4. 5.]]
"""
Como puede ver, la matriz que reconstruimos es con la que comenzamos.
Conclusión
El álgebra lineal puede ser enloquecedoramente abstracta y está llena de misterios e ideas para
reflexionar. Puede encontrar que todo el tema es una gran madriguera de conejo, ¡y tendría razón!
Sin embargo, es una buena idea seguir sintiendo curiosidad al respecto si desea tener una carrera
larga y exitosa en ciencia de datos.Es la base para la computación estadística, el aprendizaje
automático y otras áreas de ciencia de datos aplicada. En última instancia, es la base de la
105
informática en general. Ciertamente puede salirse con la suya sin saberlo por un tiempo, pero
encontrará limitaciones en su comprensión en algún momento.
Puede preguntarse cómo estas ideas son prácticas, ya que pueden parecer teóricas. No te
preocupes; veremos algunas aplicaciones prácticas a lo largo de este libro. Pero la teoría y las
interpretaciones geométricas son importantes para tener intuición cuando trabaja con datos, y al
comprender las transformaciones lineales visualmente, está preparado para asumir conceptos más
avanzados que se le pueden presentar más adelante en sus actividades.
Si desea obtener más información sobre la programación lineal, no hay mejor lugar que la lista de
reproducción de YouTube de 3Blue1Brown "Essence of Linear Algebra". Los videos de álgebra
lineal de PatrickJMT también son útiles.
Si desea sentirse más cómodo con NumPy, se recomienda leer el libro de O'Reilly Python for Data
Analysis (2.ª edición) de Wes McKinney. No se enfoca mucho en el álgebra lineal, pero brinda
instrucciones prácticas sobre el uso de NumPy, Pandas y Python en conjuntos de datos.
Ejercicios
1. El vector tiene un valor de [1, 2] pero luego ocurre una transformación. aterriza en [2,
0] y aterriza en [0, 1.5]. ¿Dónde cae ?
2. El vector tiene un valor de [1, 2] pero luego ocurre una transformación. i ^ aterriza en
[-2, 1] y aterriza en [1, -2]. ¿Dónde cae ?
3. Una transformación aterriza en [1, 0] y aterriza en [2, 2]. ¿Cuál es el determinante de
esta transformación?
4. ¿Se pueden hacer dos o más transformaciones lineales en una sola transformación lineal?
¿Por qué o por qué no?
5. Resuelva el sistema de ecuaciones para x, y y z :
3x + 1y + 0z == 54
2x + 4y + 1z = 12
3x + 1y + 8z = 6
6. ¿La siguiente matriz es linealmente dependiente? ¿Por qué o por qué no?
106
Capítulo 5. Regresión lineal
Una de las técnicas más prácticas en el análisis de datos es ajustar una línea a través de los
puntos de datos observados para mostrar una relación entre dos o más variables. Una regresión
intenta ajustar una función a los datos observados para hacer predicciones sobre nuevos datos.
Una regresión lineal ajusta una línea recta a los datos observados, intentando demostrar una
relación lineal entre las variables y hacer predicciones sobre nuevos datos aún por observar.
Podría tener más sentido ver una imagen en lugar de leer una descripción de la regresión lineal.
Hay un ejemplo de una regresión lineal en la Figura 5-1.
La regresión lineal es un caballo de batalla de la ciencia de datos y las estadísticas y no solo aplica
los conceptos que aprendimos en los capítulos anteriores, sino que establece nuevas bases para
temas posteriores como las redes neuronales ( Capítulo 7 ) y la regresión logística ( Capítulo 6 ).
Esta técnica relativamente simple existe desde hace más de doscientos años y actualmente se
considera una forma de aprendizaje automático.
Los profesionales del aprendizaje automático a menudo adoptan un enfoque diferente para la
validación, comenzando con una división de datos de prueba de entrenamiento. Es más probable
que los estadísticos utilicen métricas como intervalos de predicción y correlación para la
significancia estadística. Cubriremos ambas escuelas de pensamiento para que los lectores puedan
cerrar la brecha cada vez mayor entre las dos disciplinas y, por lo tanto, se encuenTrain mejor
equipados para usar ambos sombreros.
Figura 5-1. Ejemplo de una regresión lineal, que ajusta una línea a los datos observados
107
limitaciones de la técnica sin que los datos complejos enturbien el agua. Tracemos este conjunto de
datos como se muestra en la Figura 5-2.
Figura 5-2. Trazar una muestra de 10 perros con su edad y número de visitas al veterinario
Podemos ver claramente que hay una correlación lineal aquí, lo que significa que cuando una de
estas variables aumenta/disminuye, la otra aumenta/disminuye en una cantidad aproximadamente
proporcional. Podríamos trazar una línea a través de estos puntos para mostrar una correlación
como esta en la figura 5-3.
Mostraré cómo calcular esta línea ajustada más adelante en este capítulo. También exploraremos
cómo calcular la calidad de esta línea ajustada. Por ahora, concentrémonos en los beneficios de
realizar una regresión lineal. Nos permite hacer predicciones sobre datos que no hemos visto
antes. No tengo un perro en mi muestra que tenga 8.5 años, pero puedo mirar esta línea y estimar
que el perro tendrá 21 visitas veterinarias en su vida. Solo miro la línea donde x = 8.5 y veo que y =
21.218 como se muestra en la Figura 5-4. Otro beneficio es que podemos analizar las variables en
busca de posibles relaciones e hipotetizar que las variables correlacionadas son causales entre sí.
Ahora, ¿cuáles son las desventajas de una regresión lineal? No puedo esperar que todos los
resultados caigan exactamente en esa línea. Después de todo, los datos del mundo real son
ruidosos y nunca perfectos y no seguirán una línea recta. ¡Es posible que ni remotamente siga una
línea recta! Habrá un error alrededor de esa línea, donde el punto caerá por encima o por debajo
108
de la línea. Cubriremos esto matemáticamente cuando hablemos de los valores p, la significación
estadística y los intervalos de predicción, que describen qué tan confiable es nuestra regresión
lineal. Otro inconveniente es que no debemos usar la regresión lineal para hacer predicciones fuera
del rango de datos que tenemos, lo que significa que no debemos hacer predicciones donde x < 0 y
x > 10 porque no tenemos datos fuera de esos valores.
Figura 5-4. Haciendo una predicción usando una regresión lineal, viendo que se predice que un
perro de 8.5 años tendrá alrededor de 21.2 visitas al veterinario
109
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# Fit a line to the points
fit = LinearRegression().fit(X, Y)
# m = 1.7867224, b = -16.51923513
m = fit.coef_.flatten()
b = fit.intercept_.flatten()
print("m = {0}".format(m))
print("b = {0}".format(b))
# show in chart
plt.plot(X, Y, 'o') # scatterplot
plt.plot(X, m*X+b) # line
plt.show()
Primero importamos los datos de este CSV en GitHub. Separamos las dos columnas en conjuntos
de datos X e Y usando Pandas. Luego fit() el modelo de regresión lineal a los datos de entrada X y
los datos de salida Y. Entonces podemos obtener los coeficientes m y b que describen nuestra
función lineal ajustada.
En el gráfico, seguramente obtendrá una línea ajustada que pasa por estos puntos que se
muestran en la Figura 5-5.
¿Qué decide la mejor línea de ajuste a estos puntos? Vamos a discutir eso a continuación.
110
Figura 5-6. Los residuales son las diferencias entre la recta y los puntos.
Los puntos por encima de la línea tendrán un residuo positivo y los puntos por debajo de la línea
tendrán un residuo negativo. En otras palabras, es la diferencia sustraída entre los valores de y
pronosticados (derivados de la línea) y los valores de y reales (que provienen de los datos). Otro
nombre para los residuos son errores, porque reflejan cuán equivocada está nuestra línea al
predecir los datos.
Calculemos estas diferencias entre estos 10 puntos y la línea y = 1.93939 x + 4.73333 en el
Ejemplo 5-2 y los residuos para cada punto en el Ejemplo 5-3.
Ejemplo 5-2. Cálculo de los residuos para una línea y datos dados
import pandas as pd
# Import points
points = pd.read_csv('https://bit.ly/3goOAnt',
delimiter=",").itertuples()
# Test with a given line
m = 1.93939
b = 4.73333
# Calculate the residuals
for p in points:
y_actual = p.y
y_predict = m*p.x + b
residual = y_actual - y_predict
print(residual)
111
Si ajustamos una línea recta a través de nuestros 10 puntos de datos, probablemente queramos
minimizar estos residuos en total para que haya la menor brecha posible entre la línea y los puntos.
Pero, ¿cómo medimos el “total”? El mejor enfoque es tomar la suma de cuadrados, que
simplemente eleva al cuadrado cada residual, o multiplica cada residual por sí mismo, y los suma.
Tomamos cada valor y real y le restamos el valor y predicho tomado de la línea, luego elevamos al
cuadrado y sumamos todas esas diferencias.
En la Figura 5-7 se muestra una forma visual de pensarlo, donde superponemos un cuadrado en
cada residual y cada lado es la longitud del residual. Sumamos el área de todos estos cuadrados, y
luego aprenderemos cómo encontrar la suma mínima que podemos lograr identificando los mejores
m y b.
Figura 5-7. Visualizar la suma de cuadrados, que sería la suma de todas las áreas donde cada
cuadrado tiene una longitud de lado igual al residual
Modifiquemos nuestro código en el Ejemplo 5-4 para encontrar la suma de los cuadrados.
Ejemplo 5-4. Cálculo de la suma de cuadrados para una línea y datos dados
import pandas as pd
# Import points
points = pd.read_csv("https://bit.ly/2KF29Bd").itertuples()
# Test with a given line
m = 1.93939
b = 4.73333
sum_of_squares = 0.0
# calculate sum of squares
for p in points:
y_actual = p.y
y_predict = m*p.x + b
residual_squared = (y_predict - y_actual)**2
sum_of_squares += residual_squared
112
print("sum of squares = {}".format(sum_of_squares))
# sum of squares = 28.096969704500005
Siguiente pregunta: ¿cómo encontramos los valores m y b que producirán la suma mínima de
cuadrados, sin usar una biblioteca como scikit-learn? Veamos eso a continuación.
import pandas as pd
# Load the data
points = list(pd.read_csv('https://bit.ly/2KF29Bd',
delimiter=",").itertuples())
n = len(points)
m = (n*sum(p.x*p.y for p in points) - sum(p.x for p in points) *
sum(p.y for p in points)) / (n*sum(p.x**2 for p in points) -
sum(p.x for p in points)**2)
113
b = (sum(p.y for p in points) / n) - m * sum(p.x for p in points) / n
print(m, b)
# 1.9393939393939394 4.7333333333333325
Estas ecuaciones para calcular m y b se derivan del cálculo, y haremos algunos cálculos con
SymPy más adelante en este capítulo si tiene ganas de descubrir de dónde provienen las fórmulas.
Por ahora, puede ingresar la cantidad de puntos de datos n, así como iterar los valores x e y para
realizar las operaciones que se acaban de describir.
En el futuro, aprenderemos enfoques que están más orientados a las técnicas contemporáneas que
manejan grandes cantidades de datos. Las ecuaciones de forma cerrada tienden a no escalar bien.
COMPLEJIDAD COMPUTACIONAL
La razón por la que las ecuaciones de forma cerrada no se escalan bien con conjuntos de datos
más grandes se debe a un concepto informático llamado complejidad computacional, que mide
cuánto tiempo tarda un algoritmo a medida que crece el tamaño del problema. Puede valer la pena
familiarizarse con esto; aquí hay dos excelentes videos de YouTube sobre el tema:
“P vs. NP y el Zoológico de la Complejidad Computacional”
“¿Qué es la notación Big O?”
Notará que las operaciones transpuestas e inversas se realizan en la matriz X y se combinan con la
multiplicación de matrices. Así es como realizamos esta operación en NumPy en el Ejemplo 5-6
para obtener nuestros coeficientes m y b.
Ejemplo 5-6. Usar matrices inversas y transpuestas para ajustar una regresión lineal
import pandas as pd
from numpy.linalg import inv
import numpy as np
# Import points
df = pd.read_csv('https://bit.ly/3goOAnt', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1].flatten()
# Add placeholder "1" column to generate intercept
X_1 = np.vstack([X, np.ones(len(X))]).T
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# Calculate coefficents for slope and intercept
b = inv(X_1.transpose() @ X_1) @ (X_1.transpose() @ Y)
print(b) # [1.93939394, 4.73333333]
# Predict against the y-values
y_predict = X_1.dot(b)
114
No es intuitivo, pero tenga en cuenta que tenemos que apilar una "columna" de 1 al lado de nuestra
columna X. La razón es que esto generará el coeficiente de intersección β0. Dado que esta
columna es todo 1, genera efectivamente la intersección y no solo una pendiente β1.
Cuando tiene muchos datos con muchas dimensiones, las computadoras pueden comenzar a
ahogarse y producir resultados inestables. Este es un caso de uso para la descomposición de
matrices, que aprendimos en el Capítulo 4 sobre álgebra lineal. En este caso específico, tomamos
nuestra matriz X, agregamos una columna adicional de 1 para generar la intersección β0 como
antes, y luego la descomponemos en dos matrices de componentes Q y R :
X=Q⋅R
Evitando más madrigueras de cálculo, así es como usamos Q y R para encontrar los valores del
coeficiente beta en la forma matricial b :
b = R-1 ⋅ QT ⋅ y
Y el Ejemplo 5-7 muestra cómo usamos la fórmula de descomposición QR anterior en Python
usando NumPy para realizar una regresión lineal.
Ejemplo 5-7. Uso de la descomposición QR para realizar una regresión lineal
import pandas as pd
from numpy.linalg import qr, inv
import numpy as np
# Import points
df = pd.read_csv('https://bit.ly/3goOAnt', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1].flatten()
# Add placeholder "1" column to generate intercept
X_1 = np.vstack([X, np.ones(len(X))]).transpose()
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# calculate coefficents for slope and intercept
# using QR decomposition
Q, R = qr(X_1)
b = inv(R).dot(Q.transpose()).dot(Y)
print(b) # [1.93939394, 4.73333333]
Por lo general, la descomposición QR es el método utilizado por muchas bibliotecas científicas para
la regresión lineal porque maneja grandes cantidades de datos más fácilmente y es más estable.
¿Qué quiero decir con estable ? La estabilidad numérica es qué tan bien un algoritmo mantiene los
errores minimizados, en lugar de amplificar los errores en las aproximaciones. Recuerde que las
computadoras funcionan solo con tantos decimales y tienen que aproximar, por lo que es
importante que nuestros algoritmos no se deterioren con errores compuestos en esas
aproximaciones.
¿ABRUMADA?
Si encuentra abrumadores estos ejemplos de álgebra lineal de regresión lineal, ¡no se preocupe!
Solo quería brindar exposición a un caso de uso práctico para el álgebra lineal. En el futuro, nos
centraremos en otras técnicas que puede utilizar.
Descenso de gradiente
El descenso de gradiente es una técnica de optimización que utiliza derivadas e iteraciones para
minimizar/maximizar un conjunto de parámetros frente a un objetivo. Para aprender sobre el
descenso de gradiente, hagamos un experimento mental rápido y luego aplíquelo en un ejemplo
simple.
115
dar un paso. Da un paso en direcciones donde la pendiente desciende visiblemente. Da pasos más
grandes para pendientes más grandes y pasos más pequeños para pendientes más pequeñas. En
última instancia, se encontrará en un punto bajo donde la pendiente es plana, un valor de 0. Suena
bastante bien, ¿verdad? Este acercamiento con la linterna se conoce como descenso de pendiente,
donde pisamos en direcciones donde la pendiente va hacia abajo.
En el aprendizaje automático, a menudo pensamos en todas las posibles sumas de pérdidas
cuadradas que encontraremos con diferentes parámetros como un paisaje montañoso. Queremos
minimizar nuestra pérdida y navegamos por el panorama de la pérdida para hacerlo. Para resolver
este problema, el descenso de gradiente tiene una característica atractiva: la derivada parcial es
esa linterna, que nos permite ver las pendientes para cada parámetro (en este caso m y b, o β0
and β1). Damos un paso en direcciones para m y b donde la pendiente va hacia abajo. Damos
pasos más grandes para pendientes más grandes y pasos más pequeños para pendientes más
pequeñas. Simplemente podemos calcular la longitud de este paso tomando una fracción de la
pendiente. Esta fracción se conoce como nuestra tasa de aprendizaje. Cuanto mayor sea la tasa de
aprendizaje, más rápido se ejecutará a costa de la precisión. Pero cuanto menor sea la tasa de
aprendizaje, más tiempo llevará enTrain ar y requerirá más iteraciones.
Decidir una tasa de aprendizaje es como elegir entre una hormiga, un humano o un gigante para
bajar la pendiente. Una hormiga (velocidad de aprendizaje pequeña) dará pequeños pasos y
tardará un tiempo inaceptablemente largo en llegar al fondo, pero lo hará con precisión. Un gigante
(gran tasa de aprendizaje) puede seguir superando el mínimo hasta el punto de que nunca lo
alcance, sin importar cuántos pasos dé. El ser humano (tasa de aprendizaje moderada)
probablemente tiene el tamaño de paso más equilibrado, teniendo el intercambio correcto entre
velocidad y precisión para llegar al mínimo.
esa función. Si bien podemos resolver esto algebraicamente, usemos el gradiente descendente
para hacerlo.
Esto es visualmente lo que estamos tratando de hacer. Como se muestra en la figura 5-8,
queremos "paso" x hacia el mínimo donde la pendiente es 0.
En el ejemplo 5-8, la función f(x) y su derivada con respecto ax es dx_f(x). Recuerde que cubrimos
en el Capítulo 1 cómo usar SymPy para calcular derivadas. Después de encontrar la derivada,
procedemos a realizar un descenso de gradiente.
Ejemplo 5-8. Uso del descenso de gradiente para encontrar el mínimo de una parábola
import random
def f(x):
return (x - 3) ** 2 + 4
116
def dx_f(x):
return 2*(x - 3)
# The learning rate
L = 0.001
# The number of iterations to perform gradient descent
iterations = 100_000
# start at a random x
x = random.randint(-15,15)
for i in range(iterations):
# get slope
d_x = dx_f(x)
# update x by subtracting the (learning rate) * (slope)
x -= L * d_x
print(x, f(x)) # prints 2.999999999999889 4.0
Si graficamos la función (como se muestra en la Figura 5-8 ), deberíamos ver que el punto más
bajo de la función es claramente donde x = 3, y el código anterior debería acercarse mucho a eso.
La tasa de aprendizaje se usa para tomar una fracción de la pendiente y restarla del valor x en
cada iteración. Las pendientes más grandes darán como resultado pasos más grandes y las
pendientes más pequeñas darán como resultado pasos más pequeños. Después de suficientes
iteraciones, x terminará en el punto más bajo de la función (o lo suficientemente cerca de él) donde
la pendiente es 0.
117
D_m = sum(2 * p.x * ((m * p.x + b) - p.y) for p in points)
# slope with respect to b
D_b = sum(2 * ((m * p.x + b) - p.y) for p in points)
# update m and b¡
m -= L * D_m
b -= L * D_b
print("y = {0}x + {1}".format(m, b))
# y = 1.9393939393939548x + 4.733333333333227
Bien no está mal! Esa aproximación se acercó a nuestra solución de ecuación de forma cerrada.
¿Pero cuál es el truco? El hecho de que hayamos encontrado la "línea de mejor ajuste"
minimizando la suma de cuadrados, eso no significa que nuestra regresión lineal sea buena.
¿Minimizar la suma de cuadrados garantiza un gran modelo para hacer predicciones? No
exactamente. Ahora que le mostré cómo ajustar una regresión lineal, demos un paso atrás,
revisemos el panorama general y determinemos si una regresión lineal dada es la forma correcta
de hacer predicciones en primer lugar. Pero antes de hacer eso, aquí hay un desvío más que
muestra la solución SymPy.
Si desea aplicar nuestro conjunto de datos y ejecutar una regresión lineal mediante el descenso de
gradiente, deberá realizar algunos pasos adicionales, como se muestra en el Ejemplo 5-11.
Tendremos que sustituir los valores n, x(i) e y(i), iterando todos nuestros puntos de datos para las
funciones derivadas d_m y d_b. Eso debería dejar solo las variables m y b, en las que buscaremos
los valores óptimos usando el descenso de gradiente.
Ejemplo 5-11. Resolviendo regresión lineal usando SymP
118
import pandas as pd
from sympy import *
# Import points from CSV
points = list(pd.read_csv("https://bit.ly/2KF29Bd").itertuples())
m, b, i, n = symbols('m b i n')
x, y = symbols('x y', cls=Function)
sum_of_squares = Sum((m*x(i) + b - y(i)) ** 2, (i, 0, n))
d_m = diff(sum_of_squares, m) \
.subs(n, len(points) - 1).doit() \
.replace(x, lambda i: points[i].x) \
.replace(y, lambda i: points[i].y)
d_b = diff(sum_of_squares, b) \
.subs(n, len(points) - 1).doit() \
.replace(x, lambda i: points[i].x) \
.replace(y, lambda i: points[i].y)
# compile using lambdify for faster computation
d_m = lambdify([m, b], d_m)
d_b = lambdify([m, b], d_b)
# Building the model
m = 0.0
b = 0.0
# The learning Rate
L = .001
# The number of iterations
iterations = 100_000
# Perform Gradient Descent
for i in range(iterations):
# update m and b
m -= d_m(m,b) * L
b -= d_b(m,b) * L
print("y = {0}x + {1}".format(m, b))
# y = 1.939393939393954x + 4.733333333333231
Como se muestra en el Ejemplo 5-11, es una buena idea llamar a lambdify() en nuestras dos
funciones derivadas parciales para convertirlas de SymPy a una función de Python optimizada.
Esto hará que los cálculos se realicen mucho más rápido cuando hagamos un descenso de
gradiente. Las funciones de Python resultantes están respaldadas por NumPy, SciPy o cualquier
biblioteca numérica que SymPy detecte que tenga disponible. Después de eso, podemos realizar
un descenso de gradiente.
Finalmente, si tiene curiosidad acerca de cómo se ve la función de pérdida para esta regresión
lineal simple, el ejemplo 5-12 muestra el código SymPy que conecta los valores x, y y n en nuestra
función de pérdida y luego traza m y b como la entrada Variables. Nuestro algoritmo de descenso
de gradiente nos lleva al punto más bajo en este paisaje de pérdida que se muestra en la Figura 5-
9.
Ejemplo 5-12. Trazado de la función de pérdida para la regresión lineal
from sympy import *
from sympy.plotting import plot3d
import pandas as pd
points = list(pd.read_csv("https://bit.ly/2KF29Bd").itertuples())
m, b, i, n = symbols('m b i n')
x, y = symbols('x y', cls=Function)
sum_of_squares = Sum((m*x(i) + b - y(i)) ** 2, (i, 0, n)) \
.subs(n, len(points) - 1).doit() \
119
. replace(x, lambda i: points[i].x) \
.replace(y, lambda i: points[i].y)
plot3d(sum_of_squares)
Sobreajuste y varianza
Overfitting and Variance Adivina esto: si realmente quisiéramos minimizar la pérdida, como reducir
la suma de los cuadrados a 0, ¿qué haríamos? ¿Existen otras opciones además de la regresión
lineal? Una conclusión a la que puede llegar es simplemente ajustar una curva que toque todos los
puntos. Diablos, ¿por qué no simplemente conectar los puntos en segmentos y usarlos para hacer
predicciones como se muestra en la Figura 5-10 ? Eso nos da una pérdida de 0!
Vaya, ¿por qué pasamos por todos esos problemas con la regresión lineal y no hicimos esto en su
lugar? Bueno, recuerde que nuestro objetivo general no es minimizar la suma de cuadrados, sino
hacer predicciones precisas sobre nuevos datos. Este modelo de conectar los puntos está
severamente sobreajustado, lo que significa que moldeó la regresión a los datos de entrenamiento
con demasiada exactitud hasta el punto de que predecirá mal en los datos nuevos. Este modelo
simple de conectar los puntos es sensible a los valores atípicos que están lejos del resto de los
puntos, lo que significa que tendrá una gran variación en las predicciones. Si bien los puntos en
este ejemplo están relativamente cerca de una línea, este problema será mucho peor con otros
conjuntos de datos con más dispersión y valores atípicos. Debido a que el sobreajuste aumenta la
varianza, ¡las predicciones van a estar por todas partes!
Figura 5-10. Realizar una regresión simplemente conectando los puntos, lo que da como resultado
una pérdida cero
120
EL SOBREAJUSTE ES MEMORIZACIÓN
Cuando escucha a alguien decir que una regresión "memorizó" los datos en lugar de
generalizarlos, está hablando de sobreajuste.
Como puede adivinar, queremos encontrar generalizaciones efectivas en nuestro modelo en lugar
de memorizar datos. De lo contrario, nuestra regresión simplemente se convierte en una base de
datos donde buscamos valores.
Esta es la razón por la cual en el aprendizaje automático encontrará que se agrega sesgo al
modelo, y la regresión lineal se considera un modelo altamente sesgado. Esto no es lo mismo que
el sesgo en los datos, del que hablamos extensamente en el Capítulo 3. El sesgo en un modelo
significa que priorizamos un método (p. ej., mantener una línea recta) en lugar de doblarlo y
ajustarlo exactamente a lo que dicen los datos. Un modelo sesgado deja cierto margen de
maniobra con la esperanza de minimizar la pérdida de datos nuevos para obtener mejores
predicciones, en lugar de minimizar la pérdida de datos en los que se enTrain ó. Supongo que se
podría decir que agregar sesgo a un modelo contrarresta el sobreajuste con el ajuste insuficiente, o
que se ajuste menos a los datos de entrenamiento.
Como puedes imaginar, este es un acto de equilibrio porque se trata de dos objetivos
contradictorios. En el aprendizaje automático, básicamente decimos: “Quiero ajustar una regresión
a mis datos, pero no quiero ajustarla demasiado. Necesito algo de margen de maniobra para las
predicciones sobre nuevos datos que serán diferentes”.
El ejemplo 5-13 muestra cómo realizar un descenso de gradiente estocástico en Python. Si cambia
el tamaño de la muestra para que sea más de 1, realizará un descenso de gradiente de mini lotes.
Ejemplo 5-13. Descenso de gradiente estocástico para una regresión lineal
121
import pandas as pd
import numpy as np
# Input data
data = pd.read_csv('https://bit.ly/2KF29Bd', header=0)
X = data.iloc[:, 0].values
Y = data.iloc[:, 1].values
n = data.shape[0] # rows
# Building the model
m = 0.0
b = 0.0
sample_size = 1 # sample size
L = .0001 # The learning Rate
epochs = 1_000_000 # The number of iterations to perform gradient
descent
# Performing Stochastic Gradient Descent
for i in range(epochs):
idx = np.random.choice(n, sample_size, replace=False)
x_sample = X[idx]
y_sample = Y[idx]
# The current predicted value of Y
Y_pred = m * x_sample + b
# d/dm derivative of loss function
D_m = (-2 / sample_size) * sum(x_sample * (y_sample - Y_pred))
# d/db derivative of loss function
D_b = (-2 / sample_size) * sum(y_sample - Y_pred)
m = m - L * D_m # Update m
b = b - L * D_b # Update b
# print progress
if i % 10000 == 0:
print(i, m, b)
print("y = {0}x + {1}".format(m, b))
122
El coeficiente de correlación
Eche un vistazo a este diagrama de dispersión en la Figura 5-11 junto con su regresión lineal. ¿Por
qué una regresión lineal podría no funcionar muy bien aquí??
El problema aquí es que los datos tienen una gran variación. Si los datos están extremadamente
dispersos, aumentará la varianza hasta el punto en que las predicciones se volverán menos
precisas y útiles, lo que dará como resultado grandes residuos. Por supuesto, podemos introducir
un modelo más sesgado, como la regresión lineal, para no doblar y responder a la varianza tan
fácilmente. Sin embargo, el ajuste insuficiente también socavará nuestras predicciones porque los
datos están muy dispersos. Necesitamos medir numéricamente qué tan "apagadas" están nuestras
predicciones.
Entonces, ¿cómo se miden estos residuos en conjunto? ¿Cómo se obtiene también una idea de
cuán mala es la variación en los datos? Permítame presentarle el coeficiente de correlación,
también llamado correlación de Pearson, que mide la fuerza de la relación entre dos variables
como un valor entre -1 y 1. Un coeficiente de correlación más cercano a 0 indica que no hay
correlación. Un coeficiente de correlación más cercano a 1 indica una fuerte correlación positiva, lo
que significa que cuando una variable aumenta, la otra aumenta proporcionalmente. Si está más
cerca de -1, indica una fuerte correlación negativa, lo que significa que a medida que una variable
aumenta, la otra disminuye proporcionalmente.
Tenga en cuenta que el coeficiente de correlación a menudo se denota como r. Los datos
altamente dispersos de la figura 5-11 tienen un coeficiente de correlación de 0,1201. Dado que está
mucho más cerca de 0 que de 1, podemos inferir que los datos tienen poca correlación.
Aquí hay otros cuatro diagramas de dispersión en la figura 5-12 que muestran sus coeficientes de
correlación. Observe que cuanto más sigan los puntos una línea, más fuerte será la correlación.
Los puntos más dispersos dan como resultado correlaciones más débiles.
123
Figura 5-12. Coeficientes de correlación para cuatro diagramas de dispersión
Como puedes imaginar, el coeficiente de correlación es útil para ver si existe una posible relación
entre dos variables. Si hay una fuerte relación positiva-negativa, será útil en nuestra regresión
lineal. Si no hay una relación, es posible que solo agreguen ruido y dañen la precisión del modelo.
¿Cómo usamos Python para calcular el coeficiente de correlación? Usemos el conjunto de datos
simple de 10 puntos que usamos anteriormente. Una forma rápida y sencilla de analizar las
correlaciones de todos los pares de variables es utilizar la función corr() de Pandas. Esto facilita ver
el coeficiente de correlación entre cada par de variables en un conjunto de datos, que en este caso
solo será x e y. Esto se conoce como matriz de correlación. Eche un vistazo al ejemplo 5-14.
Ejemplo 5-14. Usando Pandas para ver el coeficiente de correlación entre cada par de variables
import pandas as pd
# Read data into Pandas dataframe
df = pd.read_csv('https://bit.ly/2KF29Bd', delimiter=",")
# Print correlations between variables
correlations = df.corr(method='pearson')
print(correlations)
# OUTPUT:
#xy
# x 1.000000 0.957586
# y 0.957586 1.000000
Como puede ver, el coeficiente de correlación 0.957586 entre x e y indica una fuerte correlación
positiva entre las dos variables. Puede ignorar las partes de la matriz donde x o y se establece en
sí mismo y tiene un valor de 1.0. Obviamente, cuando x o y se establece en sí mismo, la
correlación será perfecta en 1.0, porque los valores coinciden exactamente. Cuando tiene más de
dos variables, la matriz de correlación mostrará una cuadrícula más grande porque hay más
variables para emparejar y comparar.
Si cambia el código para usar un conjunto de datos diferente con mucha variación, donde los datos
están dispersos, verá que el coeficiente de correlación disminuye. Esto nuevamente indica una
correlación más débil.
)
Si desea ver esto completamente implementado en Python, me gusta usar bucles for de una línea
para hacer esas sumas. El ejemplo 5-15 muestra un coeficiente de correlación implementado
desde cero en Python.
Ejemplo 5-15. Cálculo del coeficiente de correlación desde cero en Python
import pandas as pd
from math import sqrt
# Import points from CSV
points = list(pd.read_csv("https://bit.ly/2KF29Bd").itertuples())
n = len(points)
numerator = n * sum(p.x * p.y for p in points) - \
sum(p.x for p in points) * sum(p.y for p in points)
denominator = sqrt(n*sum(p.x**2 for p in points) - sum(p.x for p in
points)**2) \
* sqrt(n*sum(p.y**2 for p in points) - sum(p.y for p in
points)**2)
corr = numerator / denominator
print(corr)
# OUTPUT:
# 0.9575860952087218
124
Significancia estadística
Aquí hay otro aspecto de una regresión lineal que debe considerar: ¿mi correlación de datos es
coincidente? En el Capítulo 3 estudiamos la prueba de hipótesis y los valores p, y vamos a
extender esas ideas aquí con una regresión lineal.
LA BIBLIOTECA DE MODELOS ESTADÍSTICOS
Si bien no vamos a presentar otra biblioteca más en este libro, vale la pena mencionar que
statsmodel es digno de mención si desea realizar análisis estadísticos.
Scikit-learn y otras bibliotecas de aprendizaje automático no brindan herramientas para la
significación estadística y los intervalos de confianza por razones que discutiremos en otra barra
lateral. Vamos a codificar esas técnicas nosotros mismos. ¡Pero sepa que esta biblioteca existe y
vale la pena echarle un vistazo!
Comencemos con una pregunta fundamental: ¿es posible que vea una relación lineal en mis datos
debido al azar? ¿Cómo podemos estar 95% seguros de que la correlación entre estas dos
variables es significativa y no coincidente? Si esto suena como una prueba de hipótesis del
Capítulo 3, ¡es porque lo es! Necesitamos no solo expresar el coeficiente de correlación, sino
también cuantificar qué tan seguros estamos de que el coeficiente de correlación no ocurrió por
casualidad.
En lugar de estimar una media como hicimos en el Capítulo 3 con el ejemplo de la prueba de
drogas, estamos estimando el coeficiente de correlación de la población con base en una muestra.
Denotamos el coeficiente de correlación de la población con el símbolo griego ρ (Rho), mientras
que nuestro coeficiente de correlación de la muestra es r. Tal como hicimos en el Capítulo 3,
tendremos una hipótesis nula H0 y una hipótesis alternativa H1:
H0 : ρ = 0 (implies no relationship)
H1 : ρ ≠ 0 (relationship is present)
Nuestra hipótesis nula H0 es que no hay relación entre dos variables, o más técnicamente, el
coeficiente de correlación es 0. La hipótesis alternativa H 1 es que hay una relación, y puede ser
una correlación positiva o negativa. Es por esto que la hipótesis alternativa se define como ρ ≠ 0
para sustentar una correlación tanto positiva como negativa.
Volvamos a nuestro conjunto de datos de 10 puntos como se muestra en la Figura 5-13. ¿Qué tan
probable es que veamos estos puntos de datos por casualidad? ¿Y resultan producir lo que parece
una relación lineal?
Figura 5-13. ¿Qué tan probable es que veamos estos datos, que parecen tener una correlación
lineal, por casualidad aleatoria?
Ya calculamos el coeficiente de correlación para este conjunto de datos en el ejemplo 5-14, que es
0,957586. Esa es una correlación positiva fuerte y convincente. Pero nuevamente, necesitamos
evaluar si esto fue por suerte al azar. Prosigamos nuestra prueba de hipótesis con un 95 % de
125
confianza usando una prueba de dos colas, explorando si existe una relación entre estas dos
variables.
Hablamos sobre la distribución T en el Capítulo 3, que tiene colas más anchas para capturar más
varianza e incertidumbre. Usamos una distribución T en lugar de una distribución normal para hacer
pruebas de hipótesis con regresión lineal. Primero, tracemos una distribución T con un rango de
valores críticos del 95 % como se muestra en la figura 5-14. Tomamos en cuenta el hecho de que
hay 10 registros en nuestra muestra y, por lo tanto, tenemos 9 grados de libertad (10 – 1 = 9).
Figura 5-14. Una distribución T con 9 grados de libertad, ya que hay 10 registros y restamos 1
Pongamos la prueba completa en Python como se muestra en el Ejemplo 5-17. Si nuestro valor de
prueba cae fuera del rango crítico del 95% de confianza, aceptamos que nuestra correlación no fue
por casualidad.
Ejemplo 5-17. Prueba de significación para datos de apariencia lineal
from scipy.stats import t
from math import sqrt
# sample size
n = 10
lower_cv = t(n-1).ppf(.025)
upper_cv = t(n-1).ppf(.975)
# correlation coefficient
# derived from data https://bit.ly/2KF29Bd
126
r = 0.957586
# Perform the test
test_value = r / sqrt((1-r**2) / (n-2))
print("TEST VALUE: {}".format(test_value))
print("CRITICAL RANGE: {}, {}".format(lower_cv, upper_cv))
if test_value < lower_cv or test_value > upper_cv:
print("CORRELATION PROVEN, REJECT H0")
else:
print("CORRELATION NOT PROVEN, FAILED TO REJECT H0 ")
# Calculate p-value
if test_value > 0:
p_value = 1.0 - t(n-1).cdf(test_value)
else:
p_value = t(n-1).cdf(test_value)
# Two-tailed, so multiply by 2
p_value = p_value * 2
print("P-VALUE: {}".format(p_value))
El valor de la prueba aquí es de aproximadamente 9,39956, que definitivamente está fuera del
rango de (-2,262, 2,262), por lo que podemos rechazar la hipótesis nula y decir que nuestra
correlación es real. Eso es porque el valor p es notablemente significativo:.000005976. Esto está
muy por debajo de nuestro umbral de 0,05, por lo que prácticamente no es una coincidencia: existe
una correlación. Tiene sentido que el valor p sea tan pequeño porque los puntos se parecen mucho
a una línea. Es muy poco probable que estos puntos se hayan dispuesto al azar cerca de una línea
tan cerca por casualidad.
La figura 5-15 muestra algunos otros conjuntos de datos con sus coeficientes de correlación y
valores p. Analiza cada uno de ellos. ¿Cuál es probablemente el más útil para las predicciones?
¿Cuáles son los problemas con los otros?
Ahora que ha tenido la oportunidad de trabajar en el análisis de los conjuntos de datos de la Figura
5-15, repasemos los hallazgos. La figura de la izquierda tiene una correlación positiva alta, pero
solo tiene tres puntos. La falta de datos aumenta significativamente el valor p a 0,34913 y aumenta
la probabilidad de que los datos se hayan producido por casualidad. Esto tiene sentido porque
tener solo tres puntos de datos hace probable ver un patrón lineal, pero no es mucho mejor que
tener dos puntos que simplemente conectarán una línea entre ellos. Esto trae a colación una regla
importante: tener más datos disminuirá su valor p, especialmente si esos datos gravitan hacia una
línea.
La segunda figura es lo que acabamos de cubrir. Tiene solo 10 puntos de datos, pero forma un
patrón lineal tan bien que no solo tenemos una fuerte correlación positiva sino también un valor p
extremadamente bajo. Cuando tiene un valor de p tan bajo, puede apostar que está midiendo un
proceso diseñado y estrictamente controlado, no algo sociológico o natural.
127
Las dos imágenes de la derecha en la figura 5-15 no logran identificar una relación lineal. Su
coeficiente de correlación es cercano a 0, lo que indica que no hay correlación, y los valores p,
como era de esperar, indican que la aleatoriedad desempeñó un papel.
La regla es esta: cuantos más datos tenga que se asemejen consistentemente a una línea, más
significativo será el valor p para su correlación. Cuanto más dispersos o escasos sean los datos,
más aumentará el valor p y, por lo tanto, indicará que su correlación ocurrió por casualidad.
Coeficiente de determinación
Aprendamos una métrica importante que verá mucho en estadísticas y regresiones de aprendizaje
automático. El coeficiente de determinación, llamado r 2 , mide cuánta variación en una variable se
explica por la variación de la otra variable. También es el cuadrado del coeficiente de correlación
r. A medida que r se acerca a una correlación perfecta (–1 o 1), r 2 se acerca a 1.
Esencialmente, r muestra cuánto interactúan dos variables entre sí.
2
Continuemos viendo nuestros datos de la Figura 5-13. En el Ejemplo 5-18, tome nuestro código de
marco de datos anterior que calcula el coeficiente de correlación y luego simplemente cuadrelo.
Eso multiplicará cada coeficiente de correlación por sí mismo.
Ejemplo 5-18. Creando una matriz de correlación en Pandas
import pandas as pd
# Read data into Pandas dataframe
df = pd.read_csv('https://bit.ly/2KF29Bd', delimiter=",")
# Print correlations between variables
coeff_determination = df.corr(method='pearson') ** 2
print(coeff_determination)
# OUTPUT:
#xy
# x 1.000000 0.916971
# y 0.916971 1.000000
Un coeficiente de determinación de 0,916971 se interpreta como que el 91,6971% de la variación
de x es explicada por y (y viceversa), y el 8,3029% restante es ruido provocado por otras variables
no captadas; 0,916971 es un coeficiente de determinación bastante bueno, que muestra que x e y
explican la varianza de cada uno. Pero podría haber otras variables en juego que compongan ese
0.083029 restante. Recuerde, la correlación no es igual a la causalidad, por lo que podría haber
otras variables que contribuyan a la relación que estamos viendo.
¡CORRELACIÓN NO ES CAUSALIDAD!
Es importante tener en cuenta que si bien ponemos mucho énfasis en medir la correlación y
construir métricas a su alrededor, ¡recuerde que la correlación no es causalidad! Probablemente
hayas escuchado ese mantra antes, pero quiero explicar por qué los estadísticos lo dicen.
El hecho de que veamos una correlación entre x e y no significa que x cause y. ¡En realidad podría
ser y causa x ! O tal vez hay una tercera variable z no capturada que está causando x e y. Podría
ser que x e y no se causen entre sí en absoluto y la correlación sea solo una coincidencia, por lo
que es importante que midamos la significación estadística.
Ahora tengo una pregunta más apremiante para usted. ¿Pueden las computadoras discernir entre
correlación y causalidad? La respuesta es un rotundo "¡NO!" Las computadoras tienen el concepto
de correlación pero no de causalidad. Supongamos que cargo un conjunto de datos en scikit-learn
que muestra los galones de agua consumidos y mi factura de agua. Mi computadora, o cualquier
programa que incluya scikit-learn, no tiene idea de si un mayor uso de agua provoca una factura
más alta o si una factura más alta provoca un mayor uso de agua. Un sistema de IA podría concluir
fácilmente esto último, por absurdo que sea. Esta es la razón por la que muchos proyectos de
aprendizaje automático requieren un ser humano al tanto para inyectar sentido común.
En la visión por computadora, esto también sucede. La visión por computadora a menudo usa una
regresión en los píxeles numéricos para predecir una categoría. Si enTrain o un sistema de visión
por computadora para que reconozca vacas usando imágenes de vacas, puede hacer
correlaciones fácilmente con el campo en lugar de con las cows vacas. Por lo tanto, si muestro una
imagen de un campo vacío, ¡etiquetará la hierba como vacas! Nuevamente, esto se debe a que las
computadoras no tienen un concepto de causalidad (la forma de la vaca debería causar la etiqueta
"vaca cow ") y, en cambio, se pierden en las correlaciones que no nos interesan.
128
Error estándar de la estimación
Una forma de medir el error general de una regresión lineal es el SSE, o suma de errores al
cuadrado. Aprendimos sobre esto antes, cuando elevamos al cuadrado cada residual y los
sumamos. Si (pronunciado “y-hat”) es cada valor predicho de la línea e y representa cada valor
real de y de los datos, este es el cálculo:
Sin embargo, todos estos valores cuadrados son difíciles de interpretar, por lo que
podemos usar alguna lógica de raíz cuadrada para escalar las cosas a sus unidades
originales. También promediaremos todos ellos, y esto es lo que hace el error estándar de
la estimación ( Se). Si n es el número de puntos de datos, el Ejemplo 5-19 muestra cómo
calculamos el error estándar Se en Python.
Intervalos de predicción
Como se mencionó anteriormente, nuestros datos en una regresión lineal son una muestra
de una población. Por lo tanto, nuestra regresión es tan buena como nuestra muestra.
Nuestra línea de regresión lineal también tiene una distribución normal a lo largo de ella.
Efectivamente, esto hace que cada valor de y pronosticado sea una estadística de muestra
como la media. De hecho, la "media" se desplaza a lo largo de la línea.
129
Figura 5-16. Una regresión lineal supone que una distribución normal sigue la línea
Cuando tenemos una distribución normal que sigue una línea de regresión lineal, no solo tenemos
una variable, sino también una segunda variable que dirige la distribución. Hay un intervalo de
confianza alrededor de cada predicción y, y esto se conoce como intervalo de predicción.
Traigamos un poco de contexto con nuestro ejemplo veterinario, estimando la edad de un perro y el
número de visitas al veterinario. Quiero saber el intervalo de predicción para el número de visitas al
veterinario con un 95 % de confianza para un perro de 8,5 años. El aspecto de este intervalo de
predicción se muestra en la Figura 5-17. Tenemos un 95% de confianza en que un perro de 8,5
años tendrá entre 16.462 y 25.966 visitas al veterinario.
Figura 5-17. Un intervalo de predicción para un perro que tiene 8,5 años con un 95 % de confianza
¿Cómo calculamos esto? Necesitamos obtener el margen de error y más/menos alrededor del valor
y predicho. Es una ecuación bestial que implica un valor crítico de la distribución T, así como el
error estándar de la estimación. Vamos a ver:
El valor de x que nos interesa se especifica como x 0 , que en este caso es 8.5. Así es como
resolvemos esto en Python, que se muestra en el Ejemplo 5-20.
130
Ejemplo 5-20. Cálculo de un intervalo de predicción de visitas al veterinario para un perro de 8,5
años
import pandas as pd
from scipy.stats import t
from math import sqrt
# Load the data
points = list(pd.read_csv('https://bit.ly/2KF29Bd',
delimiter=",").itertuples())
n = len(points)
# Linear Regression Line
m = 1.939
b = 4.733
# Calculate Prediction Interval for x = 8.5
x_0 = 8.5
x_mean = sum(p.x for p in points) / len(points)
t_value = t(n - 2).ppf(.975)
standard_error = sqrt(sum((p.y - (m * p.x + b)) ** 2 for p in points) /
(n - 2))
margin_of_error = t_value * standard_error * \
sqrt(1 + (1 / n) + (n * (x_0 - x_mean) ** 2) / \¡
(n * sum(p.x ** 2 for p in points) - \
sum(p.x for p in points) ** 2))
predicted_y = m*x_0 + b
# Calculate prediction interval
print(predicted_y - margin_of_error, predicted_y + margin_of_error)
# 16.462516875955465 25.966483124044537
Oye! Eso es mucho cálculo y, desafortunadamente, SciPy y otras bibliotecas de ciencia de datos
convencionales no hacen esto. Pero si te inclinas por el análisis estadístico, esta es una
información muy útil. No solo creamos una predicción basada en una regresión lineal (p. ej., un
perro de 8,5 años tendrá 21,2145 visitas al veterinario), sino que también podemos decir algo
mucho menos absoluto: hay un 95 % de probabilidad de que un perro de 8,5 años visitará al
veterinario entre 16,46 y 25,96 veces. Brillante, ¿verdad? Y es una afirmación mucho más segura
porque captura un rango en lugar de un solo valor y, por lo tanto, representa la incertidumbre.
131
sesgo de muestreo y el sobreajuste no desaparecen. Pero hay algunas prácticas que se pueden
emplear para una validación rápida.
El ejemplo 5-21 muestra cómo realizar una división de entrenamiento/prueba con scikit-learn,
donde 1/3 de los datos se reserva para la prueba y los otros 2/3 se usan para el entrenamiento.
132
Recuerde que “fitting ajustar” una regresión es sinónimo de “training entrenar”. La última palabra
es utilizada por los profesionales del aprendizaje automático.
Ejemplo 5-21. Haciendo una división de Train /prueba en regresión lineal
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
# Load the data
df = pd.read_csv('https://bit.ly/3cIH97A', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# Separate training and testing data
# This leaves a third of the data out for testing
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=1/3)
model = LinearRegression()
model.fit(X_train, Y_train)
result = model.score(X_test, Y_test)
print("r^2: %.3f" % result)
Tenga en cuenta que train_test_split() tomará nuestro conjunto de datos ( columnas X e Y ), lo
barajará y luego devolverá nuestros conjuntos de datos de entrenamiento y prueba en función del
tamaño de nuestro conjunto de datos de prueba. Usamos la función fit() de LinearRegression para
ajustar los conjuntos de datos de entrenamiento X_train y Y_train. Luego usamos la función score()
en los conjuntos de datos de prueba X_test e Y_test para evaluar el r 2, lo que nos da una idea de
cómo se comporta la regresión en datos que no se han visto antes. Cuanto mayor sea el r 2 para
nuestro conjunto de datos de prueba, mejor. Tener ese número más alto indica que la regresión
funciona bien en datos que no ha visto antes.
133
Figura 5-19. Un r 2 aplicado a diferentes conjuntos de datos de prueba con una regresión lineal
enTrain ada
También podemos alternar el conjunto de datos de prueba en cada 1/3 de pliegue. Esto se conoce
como validación cruzada y, a menudo, se considera el estándar de oro de las técnicas de
validación. La figura 5-20 muestra cómo cada 1/3 de los datos se convierte en el conjunto de datos
de prueba.
El código del Ejemplo 5-22 muestra una validación cruzada realizada en tres pliegues, y luego la
métrica de puntuación (en este caso, la suma media de los cuadrados [MSE mean sum of
squares]) se promedia junto con su desviación estándar para mostrar la consistencia de cada
prueba realizada.
Ejemplo 5-22. Uso de validación cruzada triple para una regresión lineal
import pandas as pd
from sklearn.linear_model import LinearRegression¿
from sklearn.model_selection import KFold, cross_val_score
df = pd.read_csv('https://bit.ly/3cIH97A', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)\
Y = df.values[:, -1]
# Perform a simple linear regression
kfold = KFold(n_splits=3, random_state=7, shuffle=True)
model = LinearRegression()
results = cross_val_score(model, X, Y, cv=kfold)
print(results)
print("MSE: mean=%.3f (stdev-%.3f)" % (results.mean(), results.std()))
134
No tenemos que doblar nuestros datos por tercios. Puede usar la validación de k-fold para dividir en
cualquier proporción. Por lo general, se usa 1/3, 1/5 o 1/10 para la proporción de datos de prueba,
pero 1/3 es el más común.
En general, el k que elija tiene que dar como resultado un conjunto de datos de prueba que tenga
una muestra lo suficientemente grande para el problema. También necesita suficientes conjuntos
de datos de prueba alternados/mezclados para proporcionar una estimación justa del rendimiento
de los datos que no se habían visto antes. Los conjuntos de datos de tamaño modesto pueden usar
valores k de 3, 5 o 10. La validación cruzada de dejar uno fuera (LOOCV Leaveone-out cross-
validation) alternará cada registro de datos individual como la única muestra en el conjunto de
datos de prueba, y esto puede ser útil cuando todo el conjunto de datos es pequeño.
Cuando le preocupa la varianza en su modelo, una cosa que puede hacer, en lugar de una simple
división de entrenamiento/prueba o validación cruzada, es usar la validación de pliegues aleatorios
para barajar repetidamente y dividir sus datos de entrenamiento/prueba una cantidad ilimitada de
veces. y agregar los resultados de las pruebas. En el Ejemplo 5-23 hay 10 iteraciones de muestreo
aleatorio de 1/3 de los datos para prueba y los otros 2/3 para entrenamiento. Luego, esos 10
resultados de prueba se promedian junto con sus desviaciones estándar para ver qué tan
consistentemente se desempeñan los conjuntos de datos de prueba.
¿Cuál es el truco? Es computacionalmente muy costoso ya que estamos enTrain ando la regresión
muchas veces.
Ejemplo 5-23. Uso de una validación aleatoria para una regresión lineal
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score, ShuffleSplit
df = pd.read_csv('https://bit.ly/38XwbeB', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)\
Y = df.values[:, -1]
# Perform a simple linear regression
kfold = ShuffleSplit(n_splits=10, test_size=.33, random_state=7)
model = LinearRegression()
results = cross_val_score(model, X, Y, cv=kfold)
print(results)
print("mean=%.3f (stdev-%.3f)" % (results.mean(), results.std()))
Entonces, cuando tiene poco tiempo o sus datos son demasiado voluminosos para analizarlos
estadísticamente, una división de entrenamiento/prueba proporcionará una manera de medir qué
tan bien funcionará su regresión lineal en datos que no ha visto antes.
135
Nos enfocamos casi exclusivamente en hacer una regresión lineal en una variable de entrada y una
variable de salida en este capítulo. Sin embargo, los conceptos que aprendimos aquí deberían
aplicarse en gran medida a la regresión lineal multivariable. Se pueden usar métricas como r 2, error
estándar e intervalos de confianza, pero se vuelve más difícil con más variables. El ejemplo 5-24 es
un ejemplo de una regresión lineal con dos variables de entrada y una variable de salida usando
scikit-learn.
Ejemplo 5-24. Una regresión lineal con dos variables de entrada
import pandas as pd
from sklearn.linear_model import LinearRegression
# Load the data
df = pd.read_csv('https://bit.ly/2X1HWH7', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)\
Y = df.values[:, -1]
# Training
fit = LinearRegression().fit(X, Y)
# Print coefficients
print("Coefficients = {0}".format(fit.coef_))
print("Intercept = {0}".format(fit.intercept_))
print("z = {0} + {1}x + {2}y".format(fit.intercept_, fit.coef_[0],
fit.coef_[1]))
Hay un grado de precariedad cuando un modelo se inunda tanto con variables que comienza a
perder la capacidad de explicación, y aquí es cuando las prácticas de aprendizaje automático
comienzan a aparecer y tratan al modelo como una caja negra. Espero que esté convencido de que
las preocupaciones estadísticas no desaparecen y que los datos se vuelven cada vez más escasos
a medida que agrega más variables. Pero si da un paso atrás y analiza las relaciones entre cada
par de variables utilizando una matriz de correlación, y busca comprender cómo interactúa cada par
de variables, ayudará en sus esfuerzos para crear un modelo productivo de aprendizaje
automático.
Conclusión
Cubrimos mucho en este capítulo. Intentamos ir más allá de una comprensión superficial de la
regresión lineal y hacer que las divisiones de entrenamiento/prueba sean nuestra única validación.
Quería mostrarle tanto el bisturí (estadísticas) como la motosierra (aprendizaje automático) para
que pueda juzgar cuál es mejor para un problema determinado que encuentre. Hay muchas
métricas y métodos de análisis disponibles solo en la regresión lineal, y cubrimos varios de ellos
para comprender si una regresión lineal es confiable para las predicciones. Es posible que se
encuentre en una posición para hacer regresiones como aproximaciones amplias o analizar y
combinar meticulosamente sus datos utilizando herramientas estadísticas. El enfoque que utilice
depende de la situación y, si desea obtener más información sobre las herramientas estadísticas
disponibles para Python, consulte la biblioteca statsmodel.
En el Capítulo 6 que cubre la regresión logística, revisaremos el r 2 y la significancia estadística.
Espero que este capítulo lo haya convencido de que hay formas de analizar los datos de manera
significativa y que la inversión puede marcar la diferencia en un proyecto exitoso.
Ejercicios
Aquí se proporciona un conjunto de datos de dos variables, x e y.
1. Realice una regresión lineal simple para encontrar los valores m y b que minimizan la
pérdida (suma de cuadrados).
2. Calcule el coeficiente de correlación y la significación estadística de estos datos (al 95 %
de confianza). ¿Es útil la correlación?
3. Si predigo donde x = 50, ¿cuál es el intervalo de predicción del 95% para el valor predicho
de y ?
4. Comience su regresión nuevamente y haga una división de entrenamiento/prueba.
Siéntase libre de experimentar con la validación cruzada y la validación aleatoria. ¿La
136
regresión lineal funciona bien y consistentemente en los datos de prueba? ¿Por qué o por
qué no?
Las respuestas se encuentran en el Apéndice B.
137
Capítulo 6. Regresión logística y clasificación
Logistic Regression and Classification En este capítulo vamos a cubrir la regresión logística, un tipo de
regresión que predice la probabilidad de un resultado dada una o más variables independientes.
Esto, a su vez, se puede usar para la clasificación, que predice categorías en lugar de números
reales, como hicimos con la regresión lineal.
No siempre estamos interesados en representar las variables como continuas, donde pueden
representar un número infinito de valores decimales reales. Hay situaciones en las que
preferiríamos que las variables fueran discretas o representativas de números enteros, enteros o
booleanos (1/0, true/false). La regresión logística se enTrain a en una variable de salida que es
discreta (un 1 o 0 binario) o un número categórico (que es un número entero). Emite una variable
continua en forma de probabilidad, pero eso se puede convertir en un valor discreto con un umbral.
La regresión logística es fácil de implementar y bastante resistente frente a valores atípicos y otros
desafíos de datos. Muchos problemas de aprendizaje automático se pueden resolver mejor con la
regresión logística, que ofrece más practicidad y rendimiento que otros tipos de aprendizaje
automático supervisado.
Al igual que hicimos en el Capítulo 5 cuando cubrimos la regresión lineal, intentaremos recorrer la
línea entre la estadística y el aprendizaje automático, utilizando herramientas y análisis de ambas
disciplinas. La regresión logística integrará muchos conceptos que hemos aprendido de este libro,
desde la probabilidad hasta la regresión lineal.
Figura 6-1. Trazar si los pacientes mostraron síntomas (1) o no (0) durante x horas de exposición
¿A qué tiempo los pacientes comienzan a mostrar síntomas? Bueno, es fácil de ver en casi cuatro
horas, pasamos inmediatamente de pacientes que no muestran síntomas (0) a pacientes que
muestran síntomas (1). En la Figura 6-2, vemos los mismos datos con una curva predictiva.
138
Figura 6-2. Después de cuatro horas, vemos un salto claro donde los pacientes comienzan a
mostrar síntomas.
Haciendo un análisis superficial de esta muestra, podemos decir que hay casi un 0 % de
probabilidad de que un paciente expuesto durante menos de cuatro horas muestre síntomas, pero
hay un 100 % de probabilidad durante más de cuatro horas. Entre estos dos grupos, hay un salto
inmediato para mostrar síntomas aproximadamente a las cuatro horas.
Por supuesto, nunca nada es tan claro en el mundo real. Supongamos que recopiló más datos,
donde el medio del rango tiene una combinación de pacientes que muestran síntomas versus
pacientes que no muestran síntomas, como se muestra en la Figura 6-3.
Figura 6-3. Existe una mezcla de pacientes que muestran síntomas (1) y no muestran síntomas (0)
en el medio
La forma de interpretar esto es que la probabilidad de que los pacientes muesTrain síntomas
aumenta gradualmente con cada hora de exposición. Visualicemos esto con una función logística, o
una curva en forma de S donde la variable de salida se comprime entre 0 y 1, como se muestra en
la Figura 6-4.
139
en la Figura 6-5. Si mi umbral es de al menos un 50% de probabilidad de presentar síntomas,
simplemente clasificaré que el paciente presentará síntomas.
Figura 6-5. Podemos esperar que un paciente expuesto durante seis horas tenga un 71,1 % de
probabilidades de tener síntomas, y debido a que eso es mayor que el umbral del 50 %,
predecimos que mostrarán síntomas
Función Logística
La función logística es una curva en forma de S (también conocida como curva sigmoidea ) que,
para un conjunto dado de variables de entrada, produce una variable de salida entre 0 y 1. Debido
a que la variable de salida está entre 0 y 1, se puede usar para representan una probabilidad.
Aquí está la función logística que genera una probabilidad y para una variable de entrada x :
Tenga en cuenta que esta fórmula usa el número e de Euler , que cubrimos en el Capítulo 1. La
variable x es la variable independiente/de entrada. β0 and β1 son los coeficientes que necesitamos
resolver.
β0 and β1 están empaquetados dentro de un exponente que se asemeja a una función lineal, que
puede recordar que se ve idéntica a y = mx + b or y = β0 + β1x. Esto no es una coincidencia; la
regresión logística en realidad tiene una estrecha relación con la regresión lineal, de la que
hablaremos más adelante en este capítulo. β0 de hecho es el intercepto (que llamamos b en
una regresión lineal simple) y β1 es la pendiente de x (que llamamos m en una regresión lineal
simple). Esta función lineal en el exponente se conoce como la función log-odds, pero por ahora
solo sepa que toda esta función logística produce esta curva en forma de S que necesitamos para
generar una probabilidad de cambio en un valor x.
Para declarar la función logística en Python, use la función exp() del paquete matemático para
declarar el exponente e como se muestra en el ejemplo 6-1.
Ejemplo 6-1. La función logística en Python para una variable independiente
import math
def predict_probability(x, b0, b1):
p = 1.0 / (1.0 + math.exp(-(b0 + b1 * x)))
return p
Grafiquemos para ver cómo se ve y supongamos que Β 0 = –2.823 y Β 1 = 0.62. Usaremos SymPy
en el Ejemplo 6-2 y el gráfico de salida se muestra en la Figura 6-6.
Ejemplo 6-2. Uso de SymPy para trazar una función logística
from sympy import *
b0, b1, x = symbols('b0 b1 x')
p = 1.0 / (1.0 + exp(-(b0 + b1 * x)))
p = p.subs(b0,-2.823)
p = p.subs(b1, 0.620)
140
print(p)
plot(p)
En algunos libros de texto, también puede ver la función logística declarada así:
No te preocupes por eso, porque es la misma función, solo que expresada algebraicamente de
manera diferente. Tenga en cuenta que, al igual que la regresión lineal, también podemos extender
la regresión logística a más de una variable de entrada ( x1, x2, . . . xn), como se muestra en esta
fórmula. Solo agregamos más coeficientes β x:
Usando SciPy
Lo bueno de SciPy es que los modelos a menudo tienen un conjunto estandarizado de funciones y
API, lo que significa que en muchos casos puede copiar/pegar su código y luego reutilizarlo entre
modelos. En el Ejemplo 6-3, verá una regresión logística realizada en los datos de nuestros
pacientes. Si lo compara con nuestro código de regresión lineal en el Capítulo 5, verá que tiene un
código casi idéntico para importar, separar y ajustar nuestros datos. La principal diferencia es que
uso LogisticRegression() para mi modelo en lugar de LinearRegression().
Ejemplo 6-3. Usando una regresión logística simple en SciPy
import pandas as pd
from sklearn.linear_model import LogisticRegression
# Load the data
df = pd.read_csv('https://bit.ly/33ebs2R', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)
141
Y = df.values[:, -1]
# Perform logistic regression
# Turn off penalty
model = LogisticRegression(penalty='none')
model.fit(X, Y)
# print beta1
print(model.coef_.flatten()) # 0.69267212
# print beta0
print(model.intercept_.flatten()) # -3.17576395
HACIENDO PREDICCIONES
Para hacer predicciones específicas, use las funciones predict() y predict_prob() en el objeto
modelo en SciPy, ya sea una regresión logística o cualquier otro tipo de modelo de clasificación. La
función predict() predecirá una clase específica (por ejemplo, True 1.0 o False 1.0) mientras que
predict_prob() generará probabilidades para cada clase.
Después de ejecutar el modelo en SciPy, obtengo una regresión logística donde β 0 = –3.17576395
y β 1 = 0.69267212. Cuando trace esto, debería verse bastante bien como se muestra en la Figura
6-7.
Hay un par de cosas a tener en cuenta aquí. Cuando creé el modelo LogisticRegression(), no
especifiqué ningún argumento de penalización, que elige una técnica de regularización como l1 o
l2. Si bien esto está más allá del alcance de este libro, he incluido breves conocimientos en la
siguiente nota "Aprendiendo sobre los parámetros de SciPy" para que tenga referencias útiles a
mano.
Finalmente, voy a flatten() el coeficiente y el intercepto, que resultan como matrices
multidimensionales pero con un elemento. Aplanar significa colapsar una matriz de números en
dimensiones menores, particularmente cuando hay menos elementos que dimensiones. Por
ejemplo, aquí uso flatten() para tomar un solo número anidado en una matriz bidimensional y
extraerlo como un valor único. Luego tengo mis coeficientes β0 y β1.
142
Intentaré simplificar la jerga matemática y minimizar el álgebra lineal aquí. Esencialmente, la idea
es encontrar los coeficientes β0 y β1 que lleven nuestra curva logística a esos puntos lo más cerca
posible, indicando que es más probable que haya producido esos puntos. Si recuerdas del Capítulo
2 cuando estudiamos la probabilidad, combinamos las probabilidades (o posibilidades) de múltiples
eventos multiplicándolos entre sí. En esta aplicación, estamos calculando la probabilidad de que
veamos todos estos puntos para una curva de regresión logística determinada.
Aplicando la idea de probabilidades conjuntas, cada paciente tiene una probabilidad de mostrar
síntomas según la función logística ajustada como se muestra en la Figura 6-8.
Figura 6-8. Cada valor de entrada tiene una probabilidad correspondiente en la curva logística
Obtenemos cada probabilidad de la curva de regresión logística por encima o por debajo de cada
punto. Si el punto está por debajo de la curva de regresión logística, debemos restar la probabilidad
resultante de 1,0 porque también queremos maximizar los casos falses.
Dados los coeficientes β 0 = –3,17576395 y β 1 = 0,69267212, el ejemplo 6-4 muestra cómo
calculamos la probabilidad conjunta de estos datos en Python.
Ejemplo 6-4. Cálculo de la probabilidad conjunta de observar todos los puntos para una regresión
logística dada
import math
import pandas as pd
patient_data = pd.read_csv('https://bit.ly/33ebs2R', delimiter=",").itertuples()
b0 = -3.17576395
b1 = 0.69267212
def logistic_function(x):
p = 1.0 / (1.0 + math.exp(-(b0 + b1 * x)))
return p
# Calculate the joint likelihood
joint_likelihood = 1.0
for p in patient_data:
if p.y == 1.0:
joint_likelihood *= logistic_function(p.x)
elif p.y == 0.0:
joint_likelihood *= (1.0 - logistic_function(p.x))
print(joint_likelihood) # 4.7911180221699105e-05
Aquí hay un truco matemático que podemos hacer para comprimir esa expresión if. Como cubrimos
en el Capítulo 1, cuando establece cualquier número a la potencia de 0, siempre será 1. Eche un
vistazo a esta fórmula y observe el manejo de los casos true (1) y false (0) en los exponentes:
Para hacer esto en Python, comprima todo dentro de ese bucle for en el Ejemplo 6-5.
Ejemplo 6-5. Comprimir el cálculo de probabilidad conjunta sin una expresión if
143
for p in patient_data:
joint_likelihood *= logistic_function(p.x) ** p.y * \(1.0 - logistic_function(p.x)) ** (1.0 - p.y)¿
Qué hice exactamente? Observe que hay dos mitades en esta expresión, una para cuando y = 1 y
la otra para y = 0. Cuando cualquier número se eleva al exponente 0, dará como resultado 1. Por
lo tanto, si y es 1 o 0, hará que la condición opuesta en el otro lado se evalúe como 1 y no tenga
efecto en la multiplicación. Llegamos a expresar nuestra expresión if pero lo hacemos
completamente en una expresión matemática. No podemos hacer derivadas en expresiones que
usan if, por lo que esto será útil.
Tenga en cuenta que las computadoras pueden abrumarse al multiplicar varios decimales
pequeños, lo que se conoce como subdesbordamiento de punto flotante. Esto significa que a
medida que los decimales se hacen cada vez más pequeños, lo que puede suceder en la
multiplicación, la computadora se encuentra con limitaciones para llevar la cuenta de esa cantidad
de lugares decimales. Hay un ingenioso truco matemático para evitar esto. Puede tomar el log() de
cada decimal que está multiplicando y, en su lugar, sumarlos. Esto es gracias a las propiedades
aditivas de los logaritmos que cubrimos en el Capítulo 1. Esto es más estable numéricamente, y
luego puede llamar a la función exp () para convertir la suma total nuevamente para obtener el
producto.
Revisemos nuestro código para usar la suma logarítmica en lugar de la multiplicación (vea el
Ejemplo 6-6 ). Tenga en cuenta que la función log () se establecerá de forma predeterminada en la
base e y, aunque cualquier base técnicamente funciona, esto es preferible porque e x es la
derivada de sí misma y computacionalmente será más eficiente.
Ejemplo 6-6. Usando la suma logarítmica
# Calculate the joint likelihood
joint_likelihood = 0.0
for p in patient_data:
joint_likelihood += math.log(logistic_function(p.x) ** p.y * \(1.0 - logistic_function(p.x)) ** (1.0 - p.y))
joint_likelihood = math.exp(joint_likelihood)
Así que dejemos que SymPy haga las derivadas parciales por nosotros, para β0 y β1
respectivamente. Luego los compilaremos inmediatamente y los usaremos para el descenso de
gradiente, como se muestra en el Ejemplo 6-8.
Ejemplo 6-8. Uso del descenso de gradiente en la regresión logística
from sympy import *
import pandas as pd
points = list(pd.read_csv("https://tinyurl.com/y2cocoo7").itertuples())
b1, b0, i, n = symbols('b1 b0 i n')
x, y = symbols('x y', cls=Function)
joint_likelihood = Sum(log((1.0 / (1.0 + exp(-(b0 + b1 * x(i))))) ** y(i) \
* (1.0 - (1.0 / (1.0 + exp(-(b0 + b1 * x(i)))))) ** (1 - y(i))), (i, 0, n))
# Partial derivative for m, with points substituted
d_b1 = diff(joint_likelihood, b1) \
.subs(n, len(points) - 1).doit() \
.replace(x, lambda i: points[i].x) \
144
.replace(y, lambda i: points[i].y)
# Partial derivative for m, with points substituted
d_b0 = diff(joint_likelihood, b0) \
.subs(n, len(points) - 1).doit() \
.replace(x, lambda i: points[i].x) \
.replace(y, lambda i: points[i].y)
# compile using lambdify for faster computation
d_b1 = lambdify([b1, b0], d_b1)
d_b0 = lambdify([b1, b0], d_b0)
# Perform Gradient Descent
b1 = 0.01
b0 = 0.01
L = .01
for j in range(10_000):
b1 += d_b1(b1, b0) * L
b0 += d_b0(b1, b0) * L
print(b1, b0)
# 0.6926693075370812 -3.175751550409821
Después de calcular las derivadas parciales de β0 y β1, sustituimos los valores de x e y, así como
el número de puntos de datos n. Luego usamos lambdify() para compilar la función derivada por
eficiencia (usa NumPy detrás de escena). Después de eso, realizamos un descenso de gradiente
como lo hicimos en el Capítulo 5, pero dado que estamos tratando de maximizar en lugar de
minimizar, sumamos cada ajuste a β0 y β1 en lugar de restar como en los mínimos cuadrados.
Como puede ver en el Ejemplo 6-8, obtuvimos β0 = –3.17575 y β1 = 0.692667. Esto es altamente
comparable a los valores de coeficiente que obtuvimos en SciPy anteriormente.
Como aprendimos a hacer en el Capítulo 5, también podemos usar el descenso de gradiente
estocástico y solo muestrear uno o un puñado de registros en cada iteración. Esto extendería los
beneficios de aumentar la velocidad y el rendimiento computacional y evitaría el sobreajuste. Sería
redundante cubrirlo nuevamente aquí, así que seguiremos adelante.
1 32 3 7 0
1 34 2 5 0
1 29 2 5 1
0 42 4 10 0
1 43 4 10 0
Hay 54 registros en este conjunto de datos. Digamos que queremos usarlo para predecir si otros
empleados van a renunciar y la regresión logística se puede utilizar aquí (aunque nada de esto es
una buena idea, y explicaré por qué más adelante). Recuerde que podemos admitir más de una
variable de entrada como se muestra en esta fórmula:
Crearé coeficientes β para cada una de las variables sexo, edad, promociones y
años_empleados. La variable de salida did_quit es binaria y eso impulsará el resultado de la
145
regresión logística que estamos pronosticando. Debido a que estamos tratando con múltiples
dimensiones, será difícil visualizar el hiperplano con curvas que es nuestra curva logística. Así que
nos mantendremos alejados de la visualización.
Hagámoslo interesante. Usaremos scikit-learn pero haremos un shell interactivo con el que
podamos evaluar a los empleados. El ejemplo 6-9 muestra el código y, cuando lo ejecutemos, se
realizará una regresión logística y luego podremos ingresar nuevos empleados para predecir si
renunciarán o no. ¿Qué puede ir mal? Nada, estoy seguro. Solo hacemos predicciones sobre los
atributos personales de las personas y tomamos decisiones en consecuencia. estoy seguro de que
estará bien.
(Si no quedó claro, estoy siendo muy irónico).
Ejemplo 6-9. Hacer una regresión logística multivariable en los datos de los empleados
import pandas as pd
from sklearn.linear_model import LogisticRegression
employee_data = pd.read_csv("https://tinyurl.com/y6r7qjrp")
# grab independent variable columns
inputs = employee_data.iloc[:, :-1]
# grab dependent "did_quit" variable column
output = employee_data.iloc[:, -1]
# build logistic regression
fit = LogisticRegression(penalty='none').fit(inputs, output)
# Print coefficients:
print("COEFFICIENTS: {0}".format(fit.coef_.flatten()))
print("INTERCEPT: {0}".format(fit.intercept_.flatten()))
# Interact and test with new employee data
def predict_employee_will_stay(sex, age, promotions, years_employed):
prediction = fit.predict([[sex, age, promotions, years_employed]])
probabilities = fit.predict_proba([[sex, age, promotions, years_employed]])
if prediction == [[1]]:
return "WILL LEAVE: {0}".format(probabilities)
else:
return "WILL STAY: {0}".format(probabilities)
# Test a prediction
while True:
n = input("Predict employee will stay or leave {sex},
{age},{promotions},{years employed}: ")
(sex, age, promotions, years_employed) = n.split(",")
print(predict_employee_will_stay(int(sex), int(age), int(promotions),
int(years_employed)))
La figura 6-9 muestra el resultado si se prevé que un empleado renuncie. El empleado es del sexo
“1”, tiene 34 años de edad, tuvo 1 ascenso y tiene 5 años en la empresa. Efectivamente, la
predicción es "SE irá".
Figura 6-9. Hacer una predicción de si un empleado de 34 años con 1 promoción y 5 años dejará
de trabajar
146
Tenga en cuenta que la función predict_proba() generará dos valores, el primero es la probabilidad
de 0 (false) y el segundo es 1 (true).
Notará que los coeficientes para sexo, edad, promociones y años_empleados se muestran en ese
orden. Por el peso de los coeficientes, puede ver que el sexo y la edad juegan un papel muy
pequeño en la predicción (ambos tienen un peso cercano a 0). Sin embargo, las promociones y los
años_empleados tienen pesos significativos de –2,504 y 0,97. Aquí hay un secreto con este
conjunto de datos de juguetes: lo fabriqué para que un empleado renuncie si no obtiene un ascenso
aproximadamente cada dos años. Efectivamente, mi regresión logística recogió este patrón y
también puede probarlo con otros empleados. Sin embargo, si se aventura fuera de los rangos de
datos en los que se enTrain ó, es probable que las predicciones comiencen a desmoronarse (por
ejemplo, si incluye a un empleado de 70 años que no ha sido ascendido en tres años, es difícil
decir qué este modelo servirá ya que no tiene datos alrededor de esa edad).
Por supuesto, la vida real no siempre es tan limpia. Es probable que un empleado que ha estado en
una empresa durante ocho años y nunca haya obtenido un ascenso se sienta cómodo con su
puesto y no se vaya pronto. Si ese es el caso, variables como la edad podrían desempeñar un
papel y ser ponderadas. Luego, por supuesto, podemos preocuparnos por otras variables
relevantes que no se capturan. Consulte la siguiente advertencia para obtener más información.
147
En este punto, es hora de discutir la regresión logística y de qué está hecha matemáticamente.
Esto puede ser un poco vertiginoso, así que tómate tu tiempo aquí. Si te sientes abrumado,
siempre puedes volver a visitar esta sección más tarde.
A partir de la década de 1900, siempre ha sido de interés para los matemáticos tomar una función
lineal y escalar su salida para que caiga entre 0 y 1 y, por lo tanto, sea útil para predecir la
probabilidad. El log-odds, también llamado función logit, se presta a la regresión logística para este
propósito.
¿Recuerdas que antes señalé que el valor del exponente β0+β1x es una función lineal? Mire
nuestra función logística nuevamente:
Esta función lineal que se eleva a e se conoce como la función log-odds, que toma el logaritmo de
las probabilidades para el evento de interés. Su respuesta podría ser: “Espera, no veo ningún
registro() ni probabilidades. ¡Solo veo una función lineal!” Ten paciencia conmigo, te mostraré las
matemáticas ocultas.
Como ejemplo, usemos nuestra regresión logística anterior, donde Β0 = -3,17576395 y Β 1=
0,69267212. ¿Cuál es la probabilidad de mostrar síntomas después de seis horas, donde x =
6 ? Ya sabemos cómo hacer esto: inserte estos valores en nuestra función logística:
Introducimos estos valores y generamos una probabilidad de 0,72716. Pero veamos esto desde la
perspectiva de las probabilidades. Recuerde que en el Capítulo 2 aprendimos cómo calcular
probabilidades a partir de una probabilidad:
Entonces, a las seis horas, un paciente tiene 2,66517 veces más probabilidades de mostrar
síntomas que de no mostrar síntomas.
Cuando envolvemos la función de probabilidades en un logaritmo natural (un logaritmo con base
e ), lo llamamos función logit. El resultado de esta fórmula es lo que llamamos log-odds, llamado..
sorprendentemente.. porque tomamos el logaritmo de las probabilidades:
Nuestro log-odds a las seis horas es 0.9802687. ¿Qué significa esto y por qué nos importa?
Cuando estamos en el “país de probabilidades logarítmicas”, es más fácil comparar un conjunto de
probabilidades con otro. Tratamos cualquier valor superior a 0 como una probabilidad favorable de
que suceda un evento, mientras que cualquier valor inferior a 0 está en contra de un evento. Un
log-odds de –1,05 es linealmente la misma distancia de 0 que 1,05. Sin embargo, en probabilidades
simples, los equivalentes son 0,3499 y 2,857, respectivamente, lo que no es tan interpretable. Esa
es la conveniencia de log-odds.
PROBABILIDADES Y REGISTROS
ODDS AND LOGS Los logaritmos y las probabilidades tienen una relación interesante. Las
probabilidades están en contra de un evento cuando está entre 0,0 y 1,0, pero cualquier valor
superior a 1,0 favorece el evento y se extiende a un infinito positivo. Esta falta de simetría es
incómoda. Sin embargo, los logaritmos modifican la escala de una probabilidad para que sea
completamente lineal, donde una probabilidad logarítmica de 0,0 significa probabilidades justas.
148
Una probabilidad logarítmica de –1,05 es linealmente la misma distancia de 0 que 1,05, por lo que
la comparación de probabilidades es mucho más fácil.
Josh Starmer tiene un gran video que habla sobre esta relación entre probabilidades y registros.
Recuerde que dije que la función lineal en nuestra fórmula de regresión logística β0 +β1x es
nuestra función log-odds. Mira esto:
log-odds = β0 + β1x
log-odds = -3.17576395 + 0.69267212(6)
log-odds = 0.98026877
Es el mismo valor 0.98026877 que nuestro cálculo anterior, las probabilidades de nuestra regresión
logística en x = 6 y luego tomando el log() de eso! Entonces, ¿cuál es el enlace? ¿Qué une todo
esto? Dada una probabilidad de una regresión logística p y una variable de entrada x, es esta:
Tracemos la línea log-odds junto con la regresión logística, como se muestra en la figura 6-10.
Figura 6-10. La línea log-odds se convierte en una función logística que genera una probabilidad
Cada regresión logística en realidad está respaldada por una función lineal, y esa función lineal es
una función de probabilidades logarítmicas. Observe en la figura 6-10 que cuando el log-odds es
0,0 en la línea, entonces la probabilidad de la curva logística es 0,5. Esto tiene sentido porque
cuando nuestras probabilidades son justas de 1,0, la probabilidad será de 0,50, como se muestra
en la regresión logística, y las probabilidades logarítmicas serán de 0, como se muestra en la línea.
Otro beneficio que obtenemos al observar la regresión logística desde la perspectiva de las
probabilidades es que podemos comparar el efecto entre un valor de x y otro. Digamos que quiero
entender cuánto cambian mis probabilidades entre seis y ocho horas de exposición al químico.
Puedo tomar las probabilidades a las seis horas y luego a las ocho horas, y luego relacionar las dos
probabilidades entre sí en una razón de probabilidades. Esto no debe confundirse con una
probabilidad simple que, sí, es una razón, pero no es una razón de probabilidades.
Primero encontremos las probabilidades de síntomas durante seis horas y ocho horas,
respectivamente:
149
Ahora vamos a convertirlos en probabilidades, que declararemos como o x :
Finalmente, establece las dos probabilidades entre sí como una razón de probabilidades, donde las
probabilidades de ocho horas son el numerador y las probabilidades de seis horas son el
denominador. Obtenemos un valor de aproximadamente 3.996, lo que significa que nuestras
probabilidades de mostrar síntomas aumentan en casi un factor de cuatro con dos horas
adicionales de exposición:
Encontrará este valor de razón de probabilidades de 3,996 en cualquier rango de dos horas, como
de 2 horas a 4 horas, de 4 horas a 6 horas, de 8 horas a 10 horas, etc. Siempre que sea una
brecha de dos horas, encontrará que la relación de probabilidades se mantiene constante. Diferirá
para otras longitudes de rango.
R-cuadrado
Cubrimos bastantes métricas estadísticas para la regresión lineal en el Capítulo 5, e intentaremos
hacer lo mismo para la regresión logística. Todavía nos preocupamos por muchos de los mismos
problemas que en la regresión lineal, incluido el sobreajuste y la varianza. De hecho, podemos
tomar prestadas y adaptar varias métricas de la regresión lineal y aplicarlas a la regresión logística.
Comencemos con R2.
Al igual que la regresión lineal, existe un R2 para una regresión logística determinada. Si recuerda
del Capítulo 5, el R2 indica qué tan bien una variable independiente dada explica una variable
dependiente. Aplicando esto a nuestro problema de exposición química, tiene sentido que
queramos medir cuántas horas de exposición química explican la aparición de síntomas.
Realmente no hay un consenso sobre la mejor manera de calcular el R 2 en una regresión logística,
pero una técnica popular conocida como Pseudo R2 de McFadden imita de cerca el R2 utilizado en
la regresión lineal. Usaremos esta técnica en los siguientes ejemplos y aquí está la fórmula:
150
Figura 6-11. Proyectar los valores de salida de nuevo en la curva logística
Luego podemos tomar el log() de cada una de esas probabilidades y sumarlas. Esta será la
probabilidad logarítmica del ajuste ( Ejemplo 6-10 ). Al igual que hicimos al calcular la máxima
verosimilitud, convertiremos las probabilidades "falsas" restándolas de 1,0.
Ejemplo 6-10. Cálculo de la probabilidad logarítmica del ajuste
from math import log, exp
import pandas as pd
patient_data = pd.read_csv('https://bit.ly/33ebs2R', delimiter=",").itertuples()
b0 = -3.17576395
b1 = 0.69267212
def logistic_function(x):
p = 1.0 / (1.0 + exp(-(b0 + b1 * x)))
return p
# Sum the log-likelihoods
log_likelihood_fit = 0.0
for p in patient_data:
if p.y == 1.0:
log_likelihood_fit += log(logistic_function(p.x))
elif p.y == 0.0:
log_likelihood_fit += log(1.0 - logistic_function(p.x))
print(log_likelihood_fit) # -9.946161673231583
Usando algunas multiplicaciones binarias inteligentes y comprensiones de Python, podemos
consolidar ese bucle for y la expresión if en una línea que devuelve log_likelihood_fit. Similar a lo
que hicimos en la fórmula de máxima verosimilitud, podemos usar alguna resta binaria entre los
casos true y false para eliminar matemáticamente uno u otro. En este caso, multiplicamos por 0 y,
por lo tanto, aplicamos el caso true o false, pero no ambos, a la suma correspondiente ( Ejemplo 6-
11 ).
Ejemplo 6-11. Consolidando nuestra lógica de probabilidad de registro en una sola línea
log_likelihood_fit = sum(log(logistic_function(p.x)) * p.y +log(1.0 - logistic_function(p.x)) * (1.0 - p.y)
for p in patient_data)
Si tuviéramos que expresar la probabilidad del ajuste en notación matemática, así sería. Tenga en
cuenta que f (xi ) es la función logística para una variable de entrada dada x i :
Como se calculó en los ejemplos 6-10 y 6-11, tenemos -9.9461 como nuestro logaritmo de
probabilidad del ajuste. Necesitamos un punto de datos más para calcular el R 2: el logaritmo de
probabilidad que estima sin usar ninguna variable de entrada y simplemente usa el número de
casos trues dividido por todos los casos (dejando efectivamente solo la intersección). Tenga en
cuenta que podemos contar el número de casos sintomáticos sumando todos los valores de y
juntos ∑ yi , porque solo los 1 y no los 0 contarán en la suma. Aquí está la fórmula:
151
Aquí está el equivalente de Python expandido de esta fórmula aplicada en el Ejemplo 6-12.
Ejemplo 6-12. Log probabilidad de pacientes
import pandas as pd
from math import log, exp
patient_data = list(pd.read_csv('https://bit.ly/33ebs2R', delimiter=",") \
.itertuples())
likelihood = sum(p.y for p in patient_data) / len(patient_data)
log_likelihood = 0.0
for p in patient_data:
if p.y == 1.0:
log_likelihood += log(likelihood)
elif p.y == 0.0:
log_likelihood += log(1.0 - likelihood)
print(log_likelihood) # -14.341070198709906
Para consolidar esta lógica y reflejar la fórmula, podemos comprimir el ciclo for y la expresión if en
una sola línea, usando alguna lógica de multiplicación binaria para manejar casos trues y falses (
Ejemplo 6-13 ).
Ejemplo 6-13. Consolidar la probabilidad de registro en una sola línea
log_likelihood = sum(log(likelihood)*p.y + log(1.0 - likelihood)*(1.0 - p.y) \for p in patient_data)
Finalmente, simplemente ingrese estos valores y obtenga su R 2 :
152
Bien, obtuvimos un R2 = 0.306456, entonces, ¿las horas de exposición química explican si
alguien muestra síntomas? Como aprendimos en el Capítulo 5 sobre regresión lineal, un ajuste
pobre estará más cerca de un R 2 de 0.0 y un ajuste mayor estará más cerca de 1.0. Por lo tanto,
podemos concluir que las horas de exposición son mediocres para predecir síntomas, ya que el R 2
es 0.30645. Debe haber otras variables además del tiempo de exposición que predigan mejor si
alguien presentará síntomas. Esto tiene sentido porque tenemos una gran combinación de
pacientes que muestran síntomas frente a los que no muestran síntomas para la mayoría de
nuestros datos observados, como se muestra en la Figura 6-12.
Figura 6-12. Nuestros datos tienen un R2 mediocre de 0.30645 porque hay mucha variación en el
medio de nuestra curva.
Pero si tuviéramos una división clara en nuestros datos, donde los resultados 1 y 0 están
claramente separados como se muestra en la Figura 6-13, tendríamos un R 2 perfecto de 1.0.
Figura 6-13. Esta regresión logística tiene un R 2 perfecto de 1,0 porque hay una clara división en
los resultados predichos por las horas de exposición
Valores P
Al igual que la regresión lineal, no terminamos solo porque tenemos un R 2. Necesitamos
investigar qué tan probable es que hayamos visto estos datos por casualidad en lugar de por una
relación real. Esto significa que necesitamos un valor p.
Para hacer esto, necesitaremos aprender una nueva distribución de probabilidad llamada
distribución chi-cuadrado, anotada como distribución χ2 . Es continuo y se utiliza en varias áreas de
la estadística, incluida esta.!
Si tomamos cada valor en una distribución normal estándar (media de 0 y desviación estándar de
1) y lo elevamos al cuadrado, eso nos dará la distribución χ 2 con un grado de libertad. Para
nuestros propósitos, los grados de libertad dependerán de cuántos parámetros n haya en
nuestra regresión logística, que será n - 1. Puede ver ejemplos de diferentes grados de libertad
en la Figura 6-14.
153
Figura 6-14. Una distribución de χ2 con diferentes grados de libertad
Como tenemos dos parámetros (horas de exposición y si se presentaron síntomas), nuestro grado
de libertad será 1 porque 2 - 1 = 1.
Necesitaremos el ajuste de probabilidad logarítmica y la probabilidad logarítmica tal como se
calculó en la subsección anterior sobre R2. Aquí está la fórmula que producirá el valor de χ2 que
necesitamos buscar:
χ2 = 2 (log likelihood fit) - (log likelihood)
Luego tomamos ese valor y buscamos la probabilidad de la distribución χ 2. Eso nos dará
nuestro valor p:
p-value = chi(2((log likelihood fit) - (log likelihood))
El ejemplo 6-15 muestra nuestro valor p para una regresión logística ajustada dada. Usamos el
módulo chi2 de SciPy para usar la distribución chi-cuadrado.
Ejemplo 6-15. Cálculo de un valor p para una regresión logística dada
import pandas as pd
from math import log, exp
from scipy.stats import chi2
patient_data = list(pd.read_csv('https://bit.ly/33ebs2R', delimiter=",").itertuples())
# Declare fitted logistic regression
b0 = -3.17576395
b1 = 0.69267212
def logistic_function(x):
p = 1.0 / (1.0 + exp(-(b0 + b1 * x)))
return p
# calculate the log likelihood of the fit
log_likelihood_fit = sum(log(logistic_function(p.x)) * p.y +
log(1.0 - logistic_function(p.x)) * (1.0 - p.y)
for p in patient_data)
# calculate the log likelihood without fit
likelihood = sum(p.y for p in patient_data) / len(patient_data)
log_likelihood = sum(log(likelihood) * p.y + log(1.0 - likelihood) * (1.0 - p.y) \
for p in patient_data)
# calculate p-value
chi2_input = 2 * (log_likelihood_fit - log_likelihood)
p_value = chi2.pdf(chi2_input, 1) # 1 degree of freedom (n - 1)
print(p_value) # 0.0016604875618753787
Entonces tenemos un valor p de 0.00166, y si nuestro umbral de significación es.05, decimos que
estos datos son estadísticamente significativos y no fueron por casualidad.
154
Como se trató en el Capítulo 5 sobre regresión lineal, podemos usar divisiones de
entrenamiento/prueba como una forma de validar algoritmos de aprendizaje automático. Este es el
enfoque más de aprendizaje automático para evaluar el rendimiento de una regresión logística. Si
bien es una buena idea confiar en métricas estadísticas tradicionales como R 2 y valores p,
cuando se trata de más variables, esto se vuelve menos práctico. Aquí es donde las divisiones de
entrenamiento/prueba resultan útiles una vez más. Para repasar, la Figura 6-15 visualiza una
validación cruzada triple que alterna un conjunto de datos de prueba.
Figura 6-15. Una validación cruzada triple que alterna cada tercio del conjunto de datos como un
conjunto de datos de prueba
También podemos usar la validación de plegado aleatorio, la validación cruzada dejando uno fuera
y todas las demás variantes de plegado que realizamos en el Capítulo 5. Con eso fuera del camino,
hablemos de por qué la precisión es una mala medida para la clasificación.
Matrices de confusión
Supongamos que un modelo observa a personas con el nombre "Michael" que renuncian a su
trabajo. La razón por la cual los nombres y apellidos se capturan como variables de entrada es
ciertamente cuestionable, ya que es dudoso que el nombre de alguien tenga algún impacto en su
renuncia. Sin embargo, para simplificar el ejemplo, vamos con él. Luego, el modelo predice que
cualquier persona llamada "Michael" dejará su trabajo.
Ahora aquí es donde la precisión se desmorona. Tengo cien empleados, incluido uno llamado
"Michael" y otro llamado "Sam". Se predice erróneamente que Michael renunciará, y es Sam quien
termina renunciando. ¿Cuál es la precisión de mi modelo? Es 98% porque solo hubo dos
predicciones incorrectas de cien empleados, como se visualiza en la Figura 6-16.
155
Figura 6-16. Se prevé que el empleado llamado "Michael" renuncie, pero en realidad es otro
empleado el que lo hace, lo que nos da un 98 % de precisión.
Especialmente para datos desequilibrados donde el evento de interés (por ejemplo, un empleado
que renuncia) es raro, la métrica de precisión es terriblemente engañosa para los problemas de
clasificación. Si un proveedor, consultor o científico de datos alguna vez intenta venderle un
sistema de clasificación alegando precisión, solicite una matriz de confusión.
Una matriz de confusión es una cuadrícula que desglosa las predicciones contra los resultados
reales que muestran los trues positivos, trues negativos, falses positivos (error tipo I) y falses
negativos (error tipo II). Aquí hay una matriz de confusión presentada en la Figura 6-17.
156
Figura 6-18. Agregar métricas útiles a la matriz de confusión
El ejemplo 6-17 muestra cómo usar la API de matriz de confusión en SciPy en una regresión
logística con una división de entrenamiento/prueba. Tenga en cuenta que la matriz de confusión
solo se aplica al conjunto de datos de prueba.
Ejemplo 6-17. Creación de una matriz de confusión para un conjunto de datos de prueba en SciPy
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
# Load the data
df = pd.read_csv('https://bit.ly/3cManTi', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)\
Y = df.values[:, -1]
model = LogisticRegression(solver='liblinear')
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=.33,
random_state=10)
model.fit(X_train, Y_train)
prediction = model.predict(X_test)
"""
The confusion matrix evaluates accuracy within each category.
[[truepositives falsenegatives]
[falsepositives truenegatives]]
The diagonal represents correct predictions,
so we want those to be higher
"""
157
matrix = confusion_matrix(y_true=Y_test, y_pred=prediction)
print(matrix)
Figura 6-19. Una matriz de confusión para una prueba médica que identifica una enfermedad
Nos dicen que de los pacientes que tienen un riesgo para la salud, el 99% serán identificados con
éxito (sensibilidad). Usando la matriz de confusión, podemos ver que esto se verifica
matemáticamente:
Pero, ¿y si le damos la vuelta a la condición? ¿Qué porcentaje de los que dieron positivo tienen el
riesgo para la salud (precisión)? Mientras estamos cambiando una probabilidad condicional, no
tenemos que usar el Teorema de Bayes aquí porque la matriz de confusión nos da todos los
números que necesitamos:
Bien, el 79,8 % no es terrible, y ese es el porcentaje de personas que dieron positivo y que en
realidad tienen la enfermedad. Pero pregúntese esto.. ¿qué estamos asumiendo acerca de
nuestros datos? ¿Es representativa de la población?
Algunas investigaciones rápidas encontraron que el 1% de la población en realidad tiene la
enfermedad. Hay una oportunidad de usar el Teorema de Bayes aquí. Podemos dar cuenta de la
proporción de la población que realmente tiene la enfermedad e incorporarla en los hallazgos de
nuestra matriz de confusión. Entonces descubrimos algo significativo.
158
prueba (cada una representada por un punto negro) y encontrar un equilibrio agradable entre los
trues positivos y los falses positivos.
También podemos comparar diferentes modelos de aprendizaje automático creando curvas ROC
separadas para cada uno. Por ejemplo, si en la Figura 6-21 nuestra curva superior representa una
regresión logística y la curva inferior representa un árbol de decisiones (una técnica de aprendizaje
automático que no cubrimos en este libro), podemos ver el rendimiento de ambas una al lado de la
otra. El área bajo la curva (AUC area under the curve) es una buena métrica para elegir qué
modelo usar. Dado que la curva superior (regresión logística) tiene un área mayor, esto sugiere que
es un modelo superior.
Figura 6-21. Comparación de dos modelos por su área bajo la curva (AUC area under the curve)
con sus respectivas curvas ROC
Para usar el AUC como una métrica de puntuación, cambie el parámetro de puntuación en la API
de scikit-learn para usar roc_auc como se muestra para una validación cruzada en el ejemplo 6-18.
Ejemplo 6-18. Usando el AUC como el parámetro scikit-learn
# put Scikit_learn model here
results = cross_val_score(model, X, Y, cv=kfold, scoring='roc_auc')
print("AUC: %.3f (%.3f)" % (results.mean(), results.std()))
# AUC: 0.791 (0.051)
Desequilibrio de clases
159
Class Imbalance Hay una última cosa que cubrir antes de cerrar este capítulo. Como vimos
anteriormente cuando discutimos las matrices de confusión, el desequilibrio de clases, que ocurre
cuando los datos no se representan por igual en todas las clases de resultados, es un problema en
el aprendizaje automático. Desafortunadamente, muchos problemas de interés están
desequilibrados, como la predicción de enfermedades, las brechas de seguridad, la detección de
fraudes, etc. El desequilibrio de clases sigue siendo un problema abierto sin una gran solución. Sin
embargo, hay algunas técnicas que puedes probar.
Primero, puede hacer cosas obvias como recopilar más datos o probar diferentes modelos, así
como usar matrices de confusión y curvas ROC/AUC. Todo esto ayudará a rastrear predicciones
deficientes y detectar errores de manera proactiva.
Otra técnica común es duplicar muestras en la clase minoritaria hasta que esté igualmente
representada en el conjunto de datos. Puede hacer esto en scikit-learn como se muestra en el
Ejemplo 6-19 al realizar sus divisiones de prueba de entrenamiento. Pase la opción estratificar con
la columna que contiene los valores de la clase e intentará representar por igual los datos de cada
clase.
Ejemplo 6-19. Uso de la opción de estratificación en scikit-learn para equilibrar las clases en los
datos
X, Y = ...
X_train, X_test, Y_train, Y_test = \
train_test_split(X, Y, test_size=.33, stratify=Y)
También existe una familia de algoritmos denominada SMOTE, que generan muestras sintéticas de
la clase minoritaria. Sin embargo, lo más ideal sería abordar el problema de una manera que utilice
modelos de detección de anomalías, que están diseñados deliberadamente para buscar un evento
raro. Sin embargo, estos buscan valores atípicos y no son necesariamente una clasificación, ya que
son algoritmos no supervisados. Todas estas técnicas están más allá del alcance de este libro,
pero vale la pena mencionarlas, ya que pueden brindar mejores soluciones a un problema
determinado.
Conclusión
La regresión logística es el modelo caballo de batalla para predecir probabilidades y clasificaciones
de datos. Las regresiones logísticas pueden predecir más de una categoría en lugar de solo un
true/false. Simplemente construye un modelo de regresión logística separado, ya sea que
pertenezca o no a esa categoría, y el modelo que produce la probabilidad más alta es el que gana.
Es posible que descubra que scikit-learn, en su mayor parte, hará esto por usted y detectará
cuándo sus datos tienen más de dos clases.
En este capítulo, cubrimos no solo cómo ajustar una regresión logística usando gradiente
descendente y scikit-learn, sino también enfoques estadísticos y de aprendizaje automático para la
validación. En el frente estadístico, cubrimos el R2 y el valor p, y en el aprendizaje automático
exploramos divisiones de entrenamiento/prueba, matrices de confusión y ROC/AUC.
Si desea obtener más información sobre la regresión logística, probablemente el mejor recurso
para seguir adelante es la lista de reproducción StatQuest de Josh Starmer sobre regresión
logística. Tengo que dar crédito al trabajo de Josh por ayudar en algunas partes de este capítulo,
particularmente en cómo calcular los valores R 2 y p para la regresión logística. Por lo menos,
¡mira sus videos para ver los fantásticos jingles de apertura !
Como siempre, se encontrará caminando entre los dos mundos de las estadísticas y el aprendizaje
automático. Muchos libros y recursos en este momento cubren la regresión logística desde una
perspectiva de aprendizaje automático, pero trate de buscar también recursos estadísticos. Hay
ventajas y desventajas en ambas escuelas de pensamiento, y solo puedes ganar si te adaptas a
ambas.!
Ejercicios
Aquí se proporciona un conjunto de datos de tres variables de entrada ROJO, VERDE y AZUL, así
como una variable de salida LIGHT_OR_DARK_FONT_IND. Se utilizará para predecir si una fuente
clara/oscura (0/1 respectivamente) funcionará para un color de fondo dado (especificado por
valores RGB).
160
1. Realice una regresión logística sobre los datos anteriores, utilizando la validación cruzada
triple y la precisión como métrica.
2. Producir una matriz de confusión comparando las predicciones y los datos reales.
3. Elija algunos colores de fondo diferentes (puede usar una herramienta RGB como esta ) y
vea si la regresión logística elige con sensatez una fuente clara (0) u oscura (1) para cada
uno.
4. Con base en los ejercicios anteriores, ¿piensa que la regresión logística es efectiva para
predecir una fuente clara u oscura para un color de fondo dado?
161
Capítulo 7. Redes neuronales
Una técnica de regresión y clasificación que ha disfrutado de un renacimiento en los últimos 10
años son las redes neuronales. En la definición más simple, una red neuronal es una regresión de
varias capas que contiene capas de pesos, sesgos y funciones no lineales que residen entre las
variables de entrada y las variables de salida. El aprendizaje profundo es una variante popular de
las redes neuronales que utiliza múltiples capas "ocultas" (o intermedias) de nodos que contienen
pesos y sesgos. Cada nodo se asemeja a una función lineal antes de pasar a una función no lineal
(llamada función de activación). Al igual que la regresión lineal, que aprendimos en el Capítulo 5,
las técnicas de optimización como el descenso de gradiente estocástico se utilizan para encontrar
los valores óptimos de peso y sesgo para minimizar los residuos.
Las redes neuronales ofrecen soluciones interesantes a problemas que antes eran difíciles de
resolver para las computadoras. Desde identificar objetos en imágenes hasta procesar palabras en
audio, las redes neuronales han creado herramientas que afectan nuestra vida cotidiana. Esto
incluye asistentes virtuales y motores de búsqueda, así como herramientas fotográficas en nuestros
iPhones.
Dado el alboroto de los medios y las afirmaciones audaces que dominan los titulares de las noticias
sobre las redes neuronales, puede sorprender que hayan existido desde la década de 1950. La
razón de su repentina popularidad después de 2010 se debe a la creciente disponibilidad de datos
y potencia informática. El desafío de ImageNet entre 2011 y 2015 fue probablemente el mayor
impulsor del renacimiento, aumentando el rendimiento al clasificar mil categorías en 1,4 millones de
imágenes con una precisión del 96,4 %.
Sin embargo, como cualquier técnica de aprendizaje automático, solo funciona en problemas
estrechamente definidos. Incluso los proyectos para crear autos que se conducen solos no utilizan
el aprendizaje profundo de extremo a extremo y utilizan principalmente sistemas de reglas
codificadas a mano con redes neuronales intrincadas que actúan como un "fabricante de etiquetas"
para identificar objetos en la carretera. Discutiremos esto más adelante en este capítulo para
comprender dónde se usan realmente las redes neuronales. Pero primero construiremos una red
neuronal simple en NumPy y luego usaremos scikit-learn como una implementación de biblioteca.
162
aplicándolas a problemas simples. Aprende sobre las fortalezas y limitaciones de la técnica en
lugar de distraerse con grandes conjuntos de datos. Entonces, con eso en mente, trate de no usar
redes neuronales donde los modelos más simples serán más prácticos. Romperemos esta regla en
este capítulo para entender la técnica.
Figura 7-1. Los colores de fondo claros se ven mejor con una fuente oscura y los colores de fondo
oscuros se ven mejor con una fuente clara
En informática, una forma de representar un color es con valores RGB, o los valores rojo, verde y
azul. Cada uno de estos valores está entre 0 y 255 y expresa cómo estos tres colores se mezclan
para crear el color deseado. Por ejemplo, si expresamos el RGB como (rojo, verde, azul), el naranja
oscuro tendría un RGB de (255,140,0) y el rosa sería (255,192,203). El negro sería (0,0,0) y el
blanco sería (255,255,255).
Desde una perspectiva de regresión y aprendizaje automático, tenemos tres variables de entrada
numéricas rojo, verde y azul para capturar un color de fondo determinado. Necesitamos ajustar una
función a estas variables de entrada y mostrar si se debe usar una fuente clara (1) u oscura (0)
para ese color de fondo.
163
Figura 7-2. Tenemos tres valores RGB numéricos que se usan para hacer una predicción para una
fuente clara u oscura
Esta salida de predicción expresa una probabilidad. La salida de probabilidades es el modelo más
común para la clasificación con redes neuronales. Una vez que reemplazamos RGB con sus
valores numéricos, vemos que menos de 0,5 sugerirá una fuente oscura, mientras que más de 0,5
sugerirá una fuente clara, como se muestra en la Figura 7-3.
Figura 7-3. Si ingresamos un color de fondo rosa (255,192,203), entonces las matemáticas
misteriosas recomiendan una fuente clara porque la probabilidad de salida 0.89 es mayor que 0.5
Entonces, ¿qué está pasando dentro de esa misteriosa caja negra matemática? Echemos un
vistazo a la Figura 7-4.
Nos falta otra pieza de esta red neuronal, las funciones de activación, pero llegaremos a eso en
breve. Primero entendamos lo que está pasando aquí. La primera capa de la izquierda es
simplemente una entrada de las tres variables, que en este caso son los valores rojo, verde y azul.
En la capa oculta (media), observe que producimos tres nodos, o funciones de pesos y sesgos,
entre las entradas y las salidas. Cada nodo es esencialmente una función lineal con pendientes W i
e intersecciones Bi que se multiplican y se suman con variables de entrada X i. Hay un peso W i
entre cada nodo de entrada y nodo oculto, y otro conjunto de pesos entre cada nodo oculto y nodo
de salida. Cada nodo oculto y de salida obtiene un sesgo adicional B i agregado.
Figura 7-4. La capa oculta de la red neuronal aplica valores de peso y sesgo a cada variable de
entrada, y la capa de salida aplica otro conjunto de pesos y sesgos a esa salida.
Observe que el nodo de salida repite la misma operación, tomando las salidas ponderadas y
sumadas resultantes de la capa oculta y convirtiéndolas en entradas de la capa final, donde se
aplicará otro conjunto de ponderaciones y sesgos.
En pocas palabras, esta es una regresión como la regresión lineal o logística, pero con muchos
más parámetros para resolver. Los valores de peso y sesgo son análogos a los parámetros m y b,
164
o β 1 y β 0, en una regresión lineal . Usamos el descenso de gradiente estocástico y
minimizamos la pérdida al igual que la regresión lineal, pero necesitamos una herramienta adicional
llamada retropropagación para desenredar los valores de peso W i y sesgo Bi y calcular sus
derivadas parciales usando la regla de la cadena. Llegaremos a eso más adelante en este capítulo,
pero por ahora supongamos que tenemos los valores de peso y sesgo optimizados. Primero
debemos hablar sobre las funciones de activación.
Funciones de activación
Traigamos las funciones de activación a continuación. Una función de activación es una función no
lineal que transforma o comprime los valores ponderados y sumados en un nodo, lo que ayuda a la
red neuronal a separar los datos de manera efectiva para que puedan clasificarse. Echemos un
vistazo a la Figura 7-5. Si no tiene las funciones de activación, sus capas ocultas no serán
productivas y no funcionarán mejor que una regresión lineal.
La función de activación de ReLU pondrá a cero cualquier salida negativa de los nodos ocultos. Si
los pesos, los sesgos y las entradas se multiplican y suman un número negativo, se convertirá en 0.
De lo contrario, la salida se deja sola. Aquí está el gráfico para ReLU ( Figura 7-6 ) usando SymPy (
Ejemplo 7-1 ).
Ejemplo 7-1. Trazar la función ReLU
from sympy import *
# plot relu
x = symbols('x')
165
relu = Max(0, x)
plot(relu)
ReLU es la abreviatura de "unidad lineal rectificada", pero esa es solo una forma elegante de decir
"convertir los valores negativos en 0". ReLU se ha vuelto popular para las capas ocultas en las
redes neuronales y el aprendizaje profundo debido a su velocidad y mitigación del problema del
gradiente de fuga. Los gradientes que se desvanecen ocurren cuando las pendientes de derivadas
parciales se vuelven tan pequeñas que se acercan prematuramente a 0 y hacen que el
entrenamiento se detenga en seco.
La capa de salida tiene un trabajo importante: toma las pilas de matemáticas de las capas ocultas
de la red neuronal y las convierte en un resultado interpretable, como presentar predicciones de
clasificación. La capa de salida de esta red neuronal en particular utiliza la función de activación
logística, que es una curva sigmoidea simple. Si lee el Capítulo 6, la función logística (o sigmoidea)
le resultará familiar y demuestra que la regresión logística actúa como una capa en nuestra red
neuronal. El nodo de salida pondera, sesga y suma cada uno de los valores entrantes de la capa
oculta. Después de eso, pasa el valor resultante a través de la función logística, por lo que genera
un número entre 0 y 1. Muy parecido a la regresión logística del Capítulo 6., esto representa una
probabilidad de que la entrada de color dada en la red neuronal recomiende una fuente clara. Si es
mayor o igual a 0.5, la red neuronal sugiere una fuente clara, pero menor que eso recomendará
una fuente oscura.
Aquí está el gráfico para la función logística ( Figura 7-7 ) usando SymPy ( Ejemplo 7-2 ).
Ejemplo 7-2. Función de activación logística en SymPy
from sympy import *
# plot relu
x = symbols('x')
relu = Max(0, x)
plot(relu)
166
Figura 7-7. Función de activación logística
Tenga en cuenta que cuando pasamos el valor ponderado, sesgado y sumado de un nodo a través
de una función de activación, ahora lo llamamos salida activada, lo que significa que ha sido filtrado
a través de la función de activación. Cuando la salida activada deja la capa oculta, la señal está
lista para pasar a la siguiente capa. La función de activación podría haber fortalecido, debilitado o
dejado la señal como está. De aquí es de donde viene la metáfora del cerebro y la sinapsis para las
redes neuronales.
Dado el potencial de complejidad, es posible que se pregunte si existen otras funciones de
activación. Algunos comunes se muestran en la Tabla 7-1.
Tabla 7-1. Funciones de activación comunes
Capa típica
Nombre utilizada Descripción notas
Linear l Output Deja los valores como No se usa comúnmente
están
Logistic Output Curva sigmoidea en Comprime valores entre 0 y 1, a menudo ayuda a
forma de S la clasificación binaria
Tangent Hidden tanh, curva sigmoidea Ayuda a "centrar" los datos acercando la media a
Hyperbolic en forma de S entre -1 0
y1
ReLU Hidden Convierte los valores La activación popular es más rápida que sigmoid y
negativos en 0 tanh, mitiga los problemas de gradiente de fuga y
es económica desde el punto de vista
computacional.
Leaky Hidden Multiplica los valores Variante controvertida de ReLU que margina en
ReLU negativos por 0,01 lugar de eliminar los valores negativos
softmax Output Garantiza que todos Útil para clasificaciones múltiples y salidas de
los nodos de salida reescalado para que sumen 1.0
suman 1,0
Esta no es una lista completa de funciones de activación y, en teoría, cualquier función podría ser
una función de activación en una red neuronal.
Si bien esta red neuronal aparentemente admite dos clases (fuente clara u oscura), en realidad
está modelada en una clase: si una fuente debe ser clara o no (1) o no (0). Si quisiera admitir varias
clases, podría agregar más nodos de salida para cada clase. Por ejemplo, si intenta reconocer los
dígitos escritos a mano del 0 al 9, habría 10 nodos de salida que representan la probabilidad de
que una imagen dada sea cada uno de esos números. Podría considerar usar softmax como
activación de salida cuando también tenga varias clases. La Figura 7-8 muestra un ejemplo de
tomar una imagen pixelada de un dígito, donde los píxeles se dividen como entradas de red
neuronal individuales y luego pasan a través de dos capas intermedias, y luego una capa de salida
con 10 nodos que representan probabilidades para 10 clases (por los dígitos 0–9).
167
Figura 7-8. Una red neuronal que toma cada píxel como entrada y predice qué dígito contiene la
imagen
Un ejemplo del uso del conjunto de datos MNISTen una red neuronal se puede encontrar en el
Apéndice A.
168
logistic = lambda x: 1 / (1 + np.exp(-x))
# Runs inputs through the neural network to get predicted outputs
def forward_prop(X):
Z1 = w_hidden @ X + b_hidden
A1 = relu(Z1)
Z2 = w_output @ A1 + b_output
A2 = logistic(Z2)
return Z1, A1, Z2, A2
# Calculate accuracy
test_predictions = forward_prop(X_test.transpose())[3] # grab only output layer, A2
test_comparisons = np.equal((test_predictions >= .5).flatten().astype(int), Y_test)
accuracy = sum(test_comparisons.astype(int) / X_test.shape[0])
print("ACCURACY: ", accuracy)
Un par de cosas a tener en cuenta aquí. El conjunto de datos que contiene los valores de entrada
RGB, así como el valor de salida (1 para claro y 0 para oscuro) se encuentran en este archivo CSV.
Estoy reduciendo los valores de las columnas de entrada R, G y B en un factor de 1/255 para que
estén entre 0 y 1. Esto ayudará al entrenamiento más adelante para que el espacio numérico se
comprima.
Tenga en cuenta que también separé 2/3 de los datos para el entrenamiento y 1/3 para las pruebas
con scikit-learn, que aprendimos a hacer en el Capítulo 5. n es simplemente el número de registros
de datos de entrenamiento.
Ahora dirija su atención a las líneas de código que se muestran en el Ejemplo 7-4.
Ejemplo 7-4. Las matrices de peso y los vectores de sesgo en NumPy
# Build neural network with weights and biases
# with random initialization
w_hidden = np.random.rand(3, 3)
w_output = np.random.rand(1, 3)
b_hidden = np.random.rand(3, 1)
b_output = np.random.rand(1, 1)
Estos declaran nuestros pesos y sesgos para las capas ocultas y de salida de nuestra red
neuronal. Esto puede no ser obvio todavía, pero la multiplicación de matrices hará que nuestro
código sea poderosamente simple usando álgebra lineal y NumPy.
Los pesos y sesgos se inicializarán como valores aleatorios entre 0 y 1. Veamos primero las
matrices de peso. Cuando ejecuté el código obtuve estas matrices:
Woutput = [ ]
Tenga en cuenta que Whidden son los pesos en la capa oculta. La primera fila representa los pesos
del primer nodo W1, W2 y W3. La segunda fila es el segundo nodo con pesos W4, W5, y W6. La
tercera fila es el tercer nodo con pesos W7, W8 y W 9.
La capa de salida tiene solo un nodo, lo que significa que su matriz tiene solo una fila con pesos
W10, W 11 y W 12.
¿Ves un patrón aquí? Cada nodo se representa como una fila en una matriz. Si hay tres nodos, hay
tres filas. Si hay un nodo, hay una fila. Cada columna contiene un valor de peso para ese nodo.
Veamos también los sesgos. Dado que hay un sesgo por nodo, habrá tres filas de sesgos para la
capa oculta y una fila de sesgos para la capa de salida. Solo hay un sesgo por nodo, por lo que
solo habrá una columna:
Boutput = [0.58018555]
169
Ahora comparemos estos valores de matriz con nuestra red neuronal visualizada, como se muestra
en la Figura 7-9.
Figura 7-9. Visualización de nuestra red neuronal contra los valores de la matriz de peso y sesgo
Entonces, además de ser esotéricamente compacto, ¿cuál es el beneficio de estos pesos y sesgos
en esta forma de matriz? Llamemos nuestra atención a estas líneas de código en el Ejemplo 7-5.
Ejemplo 7-5. Las funciones de activación y la función de propagación hacia adelante para nuestra
red neuronal.
# Activation functions
relu = lambda x: np.maximum(x, 0)
logistic = lambda x: 1 / (1 + np.exp(-x))
# Runs inputs through the neural network to get predicted outputs
def forward_prop(X):
Z1 = w_hidden @ X + b_hidden
A1 = relu(Z1)
Z2 = w_output @ A1 + b_output
A2 = logistic(Z2)
return Z1, A1, Z2, A2
Este código es importante porque ejecuta de forma concisa toda nuestra red neuronal mediante la
multiplicación de matrices y la multiplicación de matrices y vectores. Aprendimos acerca de estas
operaciones en el Capítulo 4. Ejecuta un color de tres entradas RGB a través de los pesos, sesgos
y funciones de activación en solo unas pocas líneas de código.
Primero declaro las funciones de activación relu() y logistic(), que literalmente toman un valor de
entrada dado y devuelven el valor de salida de la curva. La función forward_prop() ejecuta toda
nuestra red neuronal para una entrada de color dada X que contiene los valores R, G y B. Devuelve
las salidas de la matriz de cuatro etapas: Z1, A1, Z2 y A2. El “1” y el “2” indican que las operaciones
pertenecen a las capas 1 y 2 respectivamente. La "Z" indica una salida no activada de la capa y la
"A" es una salida activada de la capa.
La capa oculta está representada por Z1 y A1. Z1 son los pesos y sesgos aplicados a X. Luego, A1
toma esa salida de Z1 y la empuja a través de la función de activación ReLU. Z2 toma la salida de
A1 y aplica los pesos y sesgos de la capa de salida. Esa salida, a su vez, pasa por la función de
activación, la curva logística, y se convierte en A2. La etapa final, A2, es la probabilidad de
predicción de la capa de salida, que devuelve un valor entre 0 y 1. Lo llamamos A2 porque es la
salida "activada" de la capa 2.
Analicemos esto con más detalle comenzando con Z1 :
Z1 = WhiddenX + Bhidden
170
Figura 7-10. Aplicar los pesos y sesgos de la capa oculta a una entrada X mediante la
multiplicación de matrices y vectores, así como la suma de vectores
Ese vector Z1 es la salida sin procesar de la capa oculta, pero aún debemos pasarlo a través de la
función de activación para convertir Z 1 en A1. Suficientemente fácil. Simplemente pase cada
valor en ese vector a través de la función ReLU y nos dará A 1. Debido a que todos los valores son
positivos, no debería tener un impacto.
A1 = ReLU (Z1)
Ahora tomemos la salida A 1 de la capa oculta y pásela a través de la capa final para obtener Z 2
y luego A2. A1 se convierte en la entrada en la capa de salida.
Z2 = WoutputA1 + Boutput
Finalmente, pase este valor único en Z 2 a través de la función de activación para obtener A 2.
Esto producirá una predicción de aproximadamente 0.95425:
A2 = logistic (Z2)
A2 = logistic ([ ])
A2 = 0.954254478103241
Eso ejecuta toda nuestra red neuronal, aunque todavía no la hemos enTrain ado. Pero tómese un
momento para apreciar que hemos tomado todos estos valores de entrada, pesos, sesgos y
funciones no lineales y los hemos convertido en un solo valor que proporcionará una predicción.
Nuevamente, A2 es el resultado final que hace una predicción de si ese color de fondo necesita
una fuente clara (1) u oscura (1). Aunque nuestros pesos y sesgos aún no se han optimizado,
calculemos nuestra precisión como se muestra en el Ejemplo 7-6. Tome el conjunto de datos de
prueba X_test, transpóngalo y páselo a través de la función forward_prop() pero solo tome el vector
A2 con las predicciones para cada color de prueba. Luego compare las predicciones con los datos
reales y calcule el porcentaje de predicciones correctas.
Ejemplo 7-6. Precisión de cálculo
171
# Calculate accuracy
test_predictions = forward_prop(X_test.transpose())[3] # grab only A2
test_comparisons = np.equal((test_predictions >= .5).flatten().astype(int), Y_test)
accuracy = sum(test_comparisons.astype(int) / X_test.shape[0])
print("ACCURACY: ", accuracy)
Cuando ejecuto todo el código en el Ejemplo 7-3, obtengo aproximadamente entre un 55 % y un 67
% de precisión. Recuerde, los pesos y sesgos se generan aleatoriamente, por lo que las
respuestas variarán. Si bien esto puede parecer alto dado que los parámetros se generaron
aleatoriamente, recuerde que las predicciones de salida son binarias: claras u oscuras. Por lo tanto,
un lanzamiento de moneda al azar también podría producir este resultado para cada predicción, por
lo que este número no debería sorprender.
retropropagación
Antes de comenzar a usar el descenso de gradiente estocástico para optimizar nuestra red
neuronal, un desafío que tenemos es descubrir cómo cambiar cada uno de los valores de peso y
sesgo en consecuencia, aunque todos estén enredados para crear la variable de salida, que luego
se usa para calcular los residuos. ¿Cómo encontramos la derivada de cada variable de peso W i y
sesgo B i ? Necesitamos usar la regla de la cadena, que cubrimos en el Capítulo 1.
172
empujón en un peso o sesgo se propagará hasta la función de pérdida en la capa exterior. Aquí es
donde la regla de la cadena puede ayudarnos a determinar este impacto.
Centrémonos en encontrar la relación entre un peso de la capa de salida W 2 y la función de costo
C. Un cambio en el peso W 2, da como resultado un cambio en la salida no activada Z 2. Eso
entonces cambia la salida activada A2 , lo que cambia la función de costo C. Usando la regla de
la cadena, podemos definir la derivada de C con respecto a W2 como sigue:
Cuando multiplicamos estos tres gradientes juntos, obtenemos una medida de cuánto un cambio en
W2 cambiará la función de costo C.
Ahora calcularemos estas tres derivadas. Usemos SymPy para calcular la derivada de la función de
costo con respecto a A2 en el ejemplo 7-7.
173
Cuando ejecutamos una entrada X con los tres valores de entrada R, G y B, tendremos valores
para A1 , A2 , Z2 e y.
Tenga en cuenta que ReLU se calculó manualmente en lugar de utilizar la función diff() de SymPy.
Esto se debe a que los derivados funcionan con curvas suaves, no con esquinas irregulares que
existen en ReLU. Pero es fácil modificar eso simplemente declarando que la pendiente es 1 para
números positivos y 0 para números negativos. Esto tiene sentido porque los números negativos
tienen una línea plana con pendiente 0. Pero los números positivos se dejan como están con una
pendiente de 1 a 1.
174
Estas derivadas parciales se pueden encadenar para crear nuevas derivadas parciales con
respecto a los pesos y sesgos. Obtengamos las cuatro derivadas parciales para los pesos en W 1 ,
Usaremos estos gradientes encadenados para calcular la pendiente de la función de costo C con
respecto a W1 , B 1, W2 y B 2.
DIFERENCIACIÓN AUTOMÁTICA
Como puede ver, desenredar derivados incluso con la regla de la cadena y bibliotecas simbólicas
como SymPy sigue siendo tedioso. Es por eso que están aumentando las bibliotecas de
programación diferenciables, como la biblioteca JAX hecha por Google. Es casi idéntico a NumPy,
excepto que permite calcular derivadas de parámetros empaquetados como matrices.
Si desea obtener más información sobre la diferenciación automática, este video de YouTube lo
explica muy bien.
175
X_train, X_test, Y_train, Y_test = train_test_split(all_inputs, all_outputs,
test_size=1 / 3)
n = X_train.shape[0]
# Build neural network with weights and biases
# with random initialization
w_hidden = np.random.rand(3, 3)
w_output = np.random.rand(1, 3)
b_hidden = np.random.rand(3, 1)
b_output = np.random.rand(1, 1)
# Activation functions
relu = lambda x: np.maximum(x, 0)
logistic = lambda x: 1 / (1 + np.exp(-x))
# Runs inputs through the neural network to get predicted outputs
def forward_prop(X):
Z1 = w_hidden @ X + b_hidden
A1 = relu(Z1)
Z2 = w_output @ A1 + b_output
A2 = logistic(Z2)
return Z1, A1, Z2, A2
# Derivatives of Activation functions
d_relu = lambda x: x > 0
d_logistic = lambda x: np.exp(-x) / (1 + np.exp(-x)) ** 2
# returns slopes for weights and biases
# using chain rule
def backward_prop(Z1, A1, Z2, A2, X, Y):
dC_dA2 = 2 * A2 - 2 * Y
dA2_dZ2 = d_logistic(Z2)
dZ2_dA1 = w_output
dZ2_dW2 = A1
dZ2_dB2 = 1
dA1_dZ1 = d_relu(Z1)
dZ1_dW1 = X
dZ1_dB1 = 1
dC_dW2 = dC_dA2 @ dA2_dZ2 @ dZ2_dW2.T
dC_dB2 = dC_dA2 @ dA2_dZ2 * dZ2_dB2
dC_dA1 = dC_dA2 @ dA2_dZ2 @ dZ2_dA1
dC_dW1 = dC_dA1 @ dA1_dZ1 @ dZ1_dW1.T
dC_dB1 = dC_dA1 @ dA1_dZ1 * dZ1_dB1
return dC_dW1, dC_dB1, dC_dW2, dC_dB2
# Execute gradient descent
for i in range(100_000):
# randomly select one of the training data
idx = np.random.choice(n, 1, replace=False)
X_sample = X_train[idx].transpose()
Y_sample = Y_train[idx]
# run randomly selected training data through neural network
Z1, A1, Z2, A2 = forward_prop(X_sample)
# distribute error through backpropagation
# and return slopes for weights and biases
dW1, dB1, dW2, dB2 = backward_prop(Z1, A1, Z2, A2, X_sample, Y_sample)
# update weights and biases
w_hidden -= L * dW1
b_hidden -= L * dB1
w_output -= L * dW2
b_output -= L * dB2
176
# Calculate accuracy
test_predictions = forward_prop(X_test.transpose())[3] # grab only A2
test_comparisons = np.equal((test_predictions >= .5).flatten().astype(int), Y_test)
accuracy = sum(test_comparisons.astype(int) / X_test.shape[0])
print("ACCURACY: ", accuracy)
Están sucediendo muchas cosas aquí, pero se basan en todo lo demás que aprendimos en este
capítulo. Realizamos 100.000 iteraciones de descenso de gradiente estocástico. Al dividir los datos
de entrenamiento y prueba en 2/3 y 1/3, respectivamente, obtengo una precisión de
aproximadamente 97 a 99 % en mi conjunto de datos de prueba, dependiendo de cómo funcione la
aleatoriedad. Esto significa que después del entrenamiento, mi red neuronal identifica
correctamente entre el 97 y el 99 % de los datos de prueba con las predicciones de fuentes
claras/oscuras correctas.
La funciónbackward_prop() es clave aquí, implementando la regla de la cadena para tomar el error
en el nodo de salida (el residuo al cuadrado), y luego dividirlo y distribuirlo hacia atrás a la salida y
pesos/sesgos ocultos para obtener las pendientes con respecto a cada peso/sesgo. Luego
tomamos esas pendientes y empujamos los pesos/sesgos en el ciclo for, respectivamente,
multiplicándolos con la tasa de aprendizaje L tal como lo hicimos en los Capítulos 5 y 6. Hacemos
algunas multiplicaciones de matrices y vectores para distribuir el error hacia atrás en función de las
pendientes, y transponemos matrices y vectores cuando es necesario para que las dimensiones
entre filas y columnas coincidan.
Si desea que la red neuronal sea un poco más interactiva, aquí hay un fragmento de código en el
Ejemplo 7-12 donde podemos escribir diferentes colores de fondo (a través de un valor R, G y B) y
ver si predice una luz o fuente oscura. Añádelo al final del código anterior Ejemplo 7-11 y ¡pruébalo!
Ejemplo 7-12. Agregar un caparazón interactivo a nuestra red neuronal
# Interact and test with new colors
def predict_probability(r, g, b):
X = np.array([[r, g, b]]).transpose() / 255
Z1, A1, Z2, A2 = forward_prop(X)
return A2
def predict_font_shade(r, g, b):
output_values = predict_probability(r, g, b)
if output_values > .5:
return "DARK"
else:
return "LIGHT"
while True:
col_input = input("Predict light or dark font. Input values R,G,B: ")
(r, g, b) = col_input.split(",")
print(predict_font_shade(int(r), int(g), int(b)))
Construir su propia red neuronal desde cero requiere mucho trabajo y matemáticas, pero le da una
idea de su verdadera naturaleza. Al trabajar a través de las capas, el cálculo y el álgebra lineal,
obtenemos una idea más clara de lo que hacen las bibliotecas de aprendizaje profundo como
PyTorch y TensorFlow detrás de escena.
Como ha deducido de la lectura de este capítulo completo, hay muchas partes móviles para hacer
que una red neuronal funcione. Puede ser útil colocar un punto de interrupción en diferentes partes
del código para ver qué está haciendo cada operación de matriz. También puede transferir el
código a un Jupyter Notebook para obtener más información visual de cada paso.
3BLUE1BROWN EN BACKPROPAGATION
3Blue1Brown tiene algunos videos clásicos que hablan sobre la retropropagación y el cálculo
detrás de las redes neuronales.
Usando scikit-learn
Hay algunas funciones de red neuronal limitadas en scikit-learn. Si te tomas en serio el aprendizaje
profundo, probablemente querrás estudiar PyTorch o TensorFlow y obtener una computadora con
una GPU potente (¡hay una gran excusa para obtener la computadora para juegos que siempre
177
quisiste!). Me han dicho que todos los chicos geniales están usando PyTorch ahora. Sin embargo,
scikit-learn tiene algunos modelos convenientes disponibles, incluido el MLPClassifier, que significa
"clasificador de perceptrón multicapa". Esta es una red neuronal diseñada para la clasificación y
utiliza una activación de salida logística por defecto.
El ejemplo 7-13 es una versión scikit-learn de la aplicación de clasificación de colores de fondo que
desarrollamos. El argumento de activación especifica la capa oculta.
Ejemplo 7-13. Uso del clasificador de redes neuronales scikit-learn
import pandas as pd
# load data
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
df = pd.read_csv('https://bit.ly/3GsNzGt', delimiter=",")
# Extract input variables (all rows, all columns but last column)
# Note we should do some linear scaling here
X = (df.values[:, :-1] / 255.0)
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# Separate training and testing data
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=1/3)
nn = MLPClassifier(solver='sgd',
hidden_layer_sizes=(3, ),
activation='relu',
max_iter=100_000,
learning_rate_init=.05)
nn.fit(X_train, Y_train)
# Print weights and biases
print(nn.coefs_ )
print(nn.intercepts_)
print("Training set score: %f" % nn.score(X_train, Y_train))
print("Test set score: %f" % nn.score(X_test, Y_test))
Al ejecutar este código, obtengo una precisión del 99,3% en mis datos de prueba.
178
En otras palabras, el aprendizaje automático se adaptó al conjunto de datos de prueba y
capacitación del hospital de Stanford. Cuando se llevó a otros hospitales con maquinaria diferente,
el rendimiento se degradó significativamente debido al sobreajuste.
Los mismos desafíos ocurren con los vehículos autónomos y los autos sin conductor. ¡No es
suficiente simplemente enTrain ar una red neuronal en una señal de alto! Tiene que ser enTrain
ado en innumerables combinaciones de condiciones alrededor de esa señal de alto: buen clima,
clima lluvioso, noche y día, con graffiti, bloqueado por un árbol, en diferentes lugares, etc. En
escenarios de tráfico, piense en todos los diferentes tipos de vehículos, peatones, peatones
disfrazados y una infinidad de casos extremos que se encontrarán. Simplemente no existe una
forma eficaz de capturar cada tipo de evento que se encuentra en el camino simplemente teniendo
más pesos y sesgos en una red neuronal.
Esta es la razón por la que los propios vehículos autónomos no utilizan las redes neuronales de
forma integral. En cambio, se dividen diferentes módulos de software y sensores donde un módulo
puede usar una red neuronal para dibujar un cuadro alrededor de un objeto. Luego, otro módulo
usará una red neuronal diferente para clasificar el objeto en esa caja, como un peatón. A partir de
ahí, la lógica tradicional basada en reglas intentará predecir la ruta del peatón y la lógica codificada
elegirá entre diferentes condiciones sobre cómo reaccionar. El aprendizaje automático se limitó a la
actividad de creación de etiquetas, no a las tácticas y maniobras del vehículo. Además de eso, los
sensores básicos como el radar simplemente se detendrán si se detecta un objeto desconocido
frente al vehículo, y esta es solo otra pieza de la pila de tecnología que no utiliza el aprendizaje
automático o el aprendizaje profundo.
Esto puede resultar sorprendente dados todos los titulares de los medios sobre redes neuronales y
aprendizaje profundo que vencen a humanos en juegos como Chess and Go, o incluso superan a
pilotos en simulaciones de vuelo de combate. Es importante recordar que en entornos de
aprendizaje por refuerzo como estos, las simulaciones son mundos cerrados, donde se pueden
generar y aprender cantidades infinitas de datos etiquetados a través de un mundo finito virtual. Sin
embargo, el mundo real no es una simulación en la que podamos generar cantidades ilimitadas de
datos. Además, este no es un libro de filosofía, por lo que pasaremos las discusiones sobre si
vivimos en una simulación. ¡Lo siento, Elón! La recopilación de datos en el mundo real es costosa y
difícil. Además de eso, el mundo real está lleno de imprevisibilidad infinita y eventos raros. Todos
estos factores llevan a los profesionales del aprendizaje automático a recurrir al trabajo de ingreso
de datos para etiquetar imágenes de objetos de tráfico y otros datos. Las empresas emergentes de
vehículos autónomos a menudo tienen que combinar este tipo de trabajo de entrada de datos con
datos simulados, porque las millas y los escenarios de casos extremos necesarios para generar
datos de entrenamiento son demasiado astronómicos para recopilarlos simplemente conduciendo
una flota de vehículos millones de millas.
Todas estas son razones por las que a la investigación de IA le gusta usar juegos de mesa y
videojuegos, porque se pueden generar datos etiquetados ilimitados de manera fácil y limpia.
Francis Chollet, un ingeniero de renombre en Google que desarrolló Keras para TensorFlow (y
también escribió un gran libro, Aprendizaje profundo con Python ), compartió algunas ideas sobre
esto en un artículo de Verge :
La cuestión es que, una vez que eliges una medida, vas a tomar cualquier atajo disponible para
jugar con ella. Por ejemplo, si establece jugar al ajedrez como su medida de inteligencia (lo que
comenzamos a hacer en la década de 1970 hasta la década de 1990), terminará con un sistema
que juega al ajedrez, y eso es todo. No hay razón para suponer que será bueno para nada más.
Terminas con la búsqueda de árboles y minimax, y eso no te enseña nada sobre la inteligencia
humana. Hoy en día, buscar la habilidad en videojuegos como Dota o StarCraft como
representante de la inteligencia general cae exactamente en la misma trampa intelectual..
Si me propongo "resolver" Warcraft III a un nivel sobrehumano usando el aprendizaje profundo,
puede estar seguro de que lo lograré siempre que tenga acceso a suficiente talento de ingeniería y
poder de cómputo (que es del orden de decenas de millones de dólares por una tarea como esta).
Pero una vez que lo hubiera hecho, ¿qué habría aprendido sobre inteligencia o generalización?
Pues nada. En el mejor de los casos, habría desarrollado conocimientos de ingeniería sobre la
ampliación del aprendizaje profundo. Así que realmente no lo veo como una investigación científica
porque no nos enseña nada que no sepamos ya. No responde a ninguna pregunta abierta. Si la
pregunta era: "¿Podemos jugar X a un nivel sobrehumano?", La respuesta es definitivamente: "Sí,
siempre que pueda generar una muestra suficientemente densa de situaciones de entrenamiento y
alimentarlas en un modelo de aprendizaje profundo suficientemente expresivo.
179
Es decir, debemos tener cuidado de no combinar el rendimiento de un algoritmo en un juego con
capacidades más amplias que aún no se han resuelto. El aprendizaje automático, las redes
neuronales y el aprendizaje profundo funcionan estrechamente en problemas definidos. No pueden
razonar en términos generales o elegir sus propias tareas, o reflexionar sobre objetos que no han
visto antes. Como cualquier aplicación codificada, solo hacen aquello para lo que fueron
programadas.
Con la herramienta que haga falta, resuelve el problema. No debe haber parcialidad a las redes
neuronales o cualquier otra herramienta a su disposición. Con todo esto en mente, el uso de una
red neuronal podría no ser la mejor opción para la tarea que tiene por delante. Es importante
considerar siempre qué problema se está esforzando por resolver sin hacer de una herramienta
específica su objetivo principal. El uso del aprendizaje profundo debe ser estratégico y estar
garantizado. Ciertamente hay casos de uso, pero en la mayor parte de su trabajo diario
probablemente tendrá más éxito con modelos más simples y más sesgados como la regresión
lineal, la regresión logística o los sistemas tradicionales basados en reglas. Pero si tiene que
clasificar objetos en imágenes, y tiene el presupuesto y la mano de obra para construir ese
conjunto de datos, entonces el aprendizaje profundo será su mejor opción.
Conclusión
Las redes neuronales y el aprendizaje profundo ofrecen algunas aplicaciones interesantes, y solo
rascamos la superficie en este capítulo. Desde el reconocimiento de imágenes hasta el
procesamiento del lenguaje natural, sigue habiendo casos de uso para aplicar redes neuronales y
sus diferentes sabores de aprendizaje profundo.
Desde cero, aprendimos cómo estructurar una red neuronal simple con una capa oculta para
predecir si se debe usar una fuente clara u oscura contra un color de fondo. También aplicamos
algunos conceptos de cálculo avanzado para calcular derivadas parciales de funciones anidadas y
lo aplicamos al descenso de gradiente estocástico para enTrain ar nuestra red neuronal. También
mencionamos bibliotecas como scikit-learn. Si bien no tenemos el ancho de banda en este libro
180
para hablar sobre TensorFlow, PyTorch y aplicaciones más avanzadas, existen excelentes
recursos para ampliar su conocimiento.
3Blue1Brown tiene una lista de reproducción fantástica sobre redes neuronales y retropropagación,
y vale la pena verla varias veces. La lista de reproducción StatQuest de Josh Starmer sobre redes
neuronales también es útil, particularmente para visualizar las redes neuronales como una
manipulación múltiple. Otro gran video sobre teoría múltiple y redes neuronales se puede encontrar
aquí en El arte del problema. Finalmente, cuando esté listo para profundizar, consulte Aprendizaje
automático práctico de Aurélien Géron con Scikit-Learn, Keras y TensorFlow (O'Reilly) y
Aprendizaje profundo con Python de Francois Chollet(Manning).
Si llegó al final de este capítulo y siente que absorbió todo dentro de lo razonable, ¡felicidades! No
solo ha aprendido efectivamente probabilidad, estadística, cálculo y álgebra lineal, sino que
también lo ha aplicado a aplicaciones prácticas como regresión lineal, regresión logística y redes
neuronales. Hablaremos sobre cómo puede proceder en el próximo capítulo y comenzar una nueva
etapa de su crecimiento profesional.
Ejercicio
Aplique una red neuronal a los datos de retención de empleados con los que trabajamos en el
Capítulo 6. Puede importar los datos aquí. Intente construir esta red neuronal para que prediga
sobre este conjunto de datos y use matrices de precisión y confusión para evaluar el rendimiento.
¿Es un buen modelo para este problema? ¿Por qué o por qué no?
Si bien puede construir la red neuronal desde cero, considere usar scikit-learn, PyTorch u otra
biblioteca de aprendizaje profundo para ahorrar tiempo.
Las respuestas se encuentran en el Apéndice B.
181
Capítulo 8. Asesoramiento profesional y el camino a seguir
Career Advice and the Path Forward Al llegar al final de este libro, es una buena idea evaluar a dónde
ir desde aquí. Aprendiste e integraste una amplia encuesta de temas matemáticos aplicados:
cálculo, probabilidad, estadística y álgebra lineal. Luego, aplicó estas técnicas a aplicaciones
prácticas, incluidas la regresión lineal, la regresión logística y las redes neuronales. En este
capítulo, cubriremos cómo usar este conocimiento en el futuro mientras navegamos por el extraño,
emocionante y extrañamente diverso panorama de una carrera en ciencia de datos.Enfatizaré la
importancia de tener una dirección y un objetivo tangible hacia el cual trabajar, en lugar de
memorizar herramientas y técnicas sin un problema real en mente.
Dado que nos estamos alejando de los conceptos fundamentales y los métodos aplicados, este
capítulo tendrá un tono diferente al resto del libro. Es posible que esté esperando aprender cómo
puede aplicar estas habilidades de modelado matemático a su carrera de manera enfocada y
tangible. Sin embargo, si desea tener éxito en una carrera de ciencia de datos, tendrá que aprender
algunas habilidades más duras como SQL y programación, así como habilidades blandas para
desarrollar una conciencia profesional. Estos últimos son especialmente importantes para que no
se pierda en la profesión cambiante que es la ciencia de datos y las fuerzas invisibles del mercado
lo tomen por sorpresa.
No voy a presumir de conocer sus objetivos profesionales o lo que espera lograr con esta
información. Sin embargo, haré algunas apuestas seguras, ya que estás leyendo este libro. Me
imagino que podría estar interesado en una carrera de ciencia de datos, o ha trabajado en análisis
de datos y desea formalizar su conocimiento analítico. Tal vez provenga de una formación en
ingeniería de software y busque comprender la IA y el aprendizaje automático. Tal vez sea un
gerente de proyecto de algún tipo y descubra que necesita comprender las capacidades de un
equipo de ciencia de datos o IA para poder alcanzar el alcance en consecuencia. Tal vez solo sea
un profesional curioso que se pregunta cómo las matemáticas pueden ser útiles a un nivel práctico,
no solo académico.
Haré todo lo posible para satisfacer las preocupaciones de todos estos grupos y, con suerte,
generalizaré algunos consejos profesionales que serán útiles para la mayoría de los lectores.
Comencemos con la redefinición de la ciencia de datos. Lo hemos estudiado objetivamente y ahora
lo veremos en el contexto del desarrollo profesional y el futuro del campo.
182
ingeniería de software.. solo por nombrar algunas. Prácticamente cualquier disciplina que toque
datos puede calificarse como "ciencia de datos". Esta falta de definición clara ha sido problemática
para el campo. Después de todo, cualquier cosa que carezca de definición está abierta a la
interpretación, como una obra de arte abstracto. Esta es la razón por la que los departamentos de
recursos humanos luchan con las ofertas de trabajo de "científico de datos", ya que tienden a estar
en todo el mapa. La figura 8-1 muestra un paraguas que abarca diferentes disciplinas y
herramientas que pueden incluirse en la ciencia de datos.
¿Cómo llegamos aquí? ¿Y cómo algo que carece de definición como la ciencia de datos puede
convertirse en una fuerza tan convincente en el mundo empresarial? Lo más importante, ¿cómo
afecta su definición (o la falta de ella) a su carrera? Todas estas son preguntas importantes de las
que hablamos en este capítulo.
Es por eso que les digo a mis clientes que una mejor definición para la ciencia de datos es la
ingeniería de software con competencia en estadísticas, aprendizaje automático y optimización. Si
elimina cualquiera de esas cuatro disciplinas (ingeniería de software, estadísticas, aprendizaje
automático y optimización), el científico de datos corre el riesgo de no rendir. La mayoría de las
organizaciones se han esforzado por hacer distinciones claras sobre los conjuntos de habilidades
que hacen que un científico de datos sea efectivo, pero la definición proporcionada debería brindar
claridad. Si bien algunos pueden pensar que la ingeniería de software es un requisito controvertido,
183
creo que es muy necesario dada la dirección que están tomando las industrias. Abordaremos este
punto más adelante.
Pero primero, la mejor manera de entender la ciencia de datos es rastrear la historia del término.
Una breve historia de la ciencia de datos
Podríamos rastrear la ciencia de datos hasta el origen de las estadísticas, ya en el siglo XVII o
incluso en el siglo VIII. En aras de la brevedad, comencemos en la década de 1990. Los analistas,
estadísticos, investigadores, "cuantitativos" e ingenieros de datos a menudo mantuvieron roles
distintos. Las pilas de herramientas a menudo consistían en hojas de cálculo, R, MATLAB, SAS y
SQL.
Por supuesto, las cosas comenzaron a cambiar rápidamente después del año 2000. Internet y los
dispositivos conectados comenzaron a generar enormes cantidades de datos. Junto con el inicio de
Hadoop, Google llevó el análisis y la recopilación de datos a alturas inimaginables. A medida que
se acercaba el 2010, los ejecutivos de Google insistieron en que los estadísticos tendrán el trabajo
"sexy" en la próxima década. Lo que vino después fue profético.
En 2012, Harvard Business Review incorporó este concepto llamado ciencia de datos y lo declaró
el “trabajo más sexy del siglo XXI”. Después del artículo de Harvard Business Review, muchas
empresas y trabajadores corporativos se apresuraron a llenar el vacío de la ciencia de datos. Los
consultores de gestión estaban preparados y posicionados para educar a los líderes de Fortune
500 sobre cómo llevar la ciencia de datos a sus organizaciones. Los desarrolladores, analistas,
investigadores, estadísticos, estadísticos, ingenieros, físicos y una miríada de otros profesionales
de SQL se rebautizaron a sí mismos como "científicos de datos". Las empresas tecnológicas,
sintiendo que los títulos de roles tradicionales como "analista", "estadístico" o "investigador"
sonaban anticuados, cambiaron el nombre de los roles a "científico de datos".
Naturalmente, la gerencia de las empresas Fortune 500 se vio presionada por el C-suite para
subirse al carro de la ciencia de datos. El razonamiento inicial era que se recopilaban muchos
datos, por lo que el big data se estaba convirtiendo en una tendencia y se necesitaban científicos
de datos para obtener información de ellos. Alrededor de este tiempo, la palabra "basado en datos"
se convirtió en una máxima en todas las industrias. El mundo empresarial creía que, a diferencia de
las personas, los datos son objetivos e imparciales.
184
está dentro y fuera del alcance de la ciencia de datos. ¿Es solo un cambio de marca caprichoso de
las estadísticas? No lo creo, pero tampoco tengo una definición completa. Creo que la reciente
abundancia de datos ha provocado algo nuevo en el mundo, y cuando miro a mi alrededor veo
personas con características compartidas que no encajan en las categorías tradicionales. Estas
personas tienden a trabajar más allá de las especialidades limitadas que dominan el mundo
corporativo e institucional, manejando todo, desde encontrar los datos, procesarlos a escala,
visualizarlos y escribirlos como una historia. También parecen comenzar observando lo que los
datos pueden decirles y luego eligiendo hilos interesantes para seguir,
Pete, irónicamente, tampoco pudo encontrar una definición para la ciencia de datos, pero
claramente explicó por qué la ciencia de datos es defectuosa pero útil. También destacó el cambio
de la investigación que abandona el método científico a favor de prácticas que alguna vez se
evitaron como la minería de datos, de la que hablamos en el Capítulo 3.
La ciencia de datos dio un giro interesante solo unos años después del artículo de Harvard
Business Review. Quizás fue más una fusión con la IA y el aprendizaje automático que un giro.
Independientemente, cuando el aprendizaje automático y el aprendizaje profundo dominaron los
titulares alrededor de 2014, los datos se vendieron como el "combustible" para crear inteligencia
artificial. Esto amplió naturalmente el alcance de la ciencia de datos y creó una confluencia con el
movimiento de aprendizaje automático/IA. En particular, el desafío de ImageNet despertó un
renovado interés en la IA y provocó un renacimiento del aprendizaje automático y el aprendizaje
profundo. Empresas como Waymo y Tesla prometieron autos sin conductor en cuestión de años
gracias a los avances en el aprendizaje profundo, lo que alimentó aún más los titulares de los
medios y las inscripciones en los campamentos de entrenamiento.
Este repentino interés en las redes neuronales y el aprendizaje profundo tuvo un efecto secundario
interesante. Las técnicas de regresión como los árboles de decisión, las máquinas de vectores de
soporte y la regresión logística, que han pasado décadas escondidas en la academia y las
profesiones estadísticas especializadas, se montaron en los faldones del aprendizaje profundo para
convertirse en el centro de atención del público. Al mismo tiempo, las bibliotecas como scikit-learn
crearon una barrera baja para ingresar al campo. Esto tenía un costo oculto de crear profesionales
de la ciencia de datos que no entendían cómo funcionaban estas bibliotecas o modelos, pero los
usaron de todos modos.
Debido a que la disciplina de la ciencia de datos ha evolucionado más rápido que la necesidad
percibida de definirla, no es raro que un rol de científico de datos sea un comodín completo. Conocí
a varias personas que tenían un título de científico de datos en compañías Fortune 500. Algunos
son altamente competentes en codificación e incluso pueden tener experiencia en ingeniería de
software, pero no tienen idea de qué es la importancia estadística. Otros permanecen confinados
en Excel y apenas saben SQL, mucho menos Python o R. Conocí a gente de ciencia de datos que
aprendieron algunas funciones en scikit-learn y rápidamente se encontraron tambaleándose porque
eso era todo lo que sabían.
¿Entonces que significa esto para usted? ¿Cómo floreces en un paisaje tan caótico y lleno de
palabras? Todo se reduce a qué tipos de problemas o industrias le interesan, y no depender
rápidamente de los empleadores para definir los roles. No es necesario ser un científico de datos
para hacer ciencia de datos. Hay una amplia gama de campos en los que puede trabajar a su favor
dado este conocimiento que ahora tiene. Puede ser analista, investigador, ingeniero de aprendizaje
automático, asesor, consultor y muchos otros roles que no necesariamente se denominan
científicos de datos.
Pero primero, abordemos algunas formas en que puede continuar aprendiendo y encontrar su
ventaja en el mercado laboral de ciencia de datos.
Encontrar su borde
El profesional práctico de la ciencia de datos necesita más que una comprensión de las
estadísticas y el aprendizaje automático para prosperar. En la mayoría de los casos, no es
razonable esperar que los datos estén fácilmente disponibles para el aprendizaje automático y
otros proyectos. En cambio, se encontrará persiguiendo fuentes de datos, guiones de ingeniería y
software, raspando documentos, raspando libros de Excel e incluso creando sus propias bases de
datos. Al menos el 95% de sus esfuerzos de codificación no estarán relacionados con el
aprendizaje automático o el modelado estadístico, sino con la creación, el movimiento y la
transformación de datos para que sean utilizables.
185
Además de eso, debe ser consciente del panorama general y la dinámica de su organización. Sus
gerentes pueden hacer suposiciones al definir su rol, y es importante identificar estas suposiciones
para que reconozca cómo lo afectan. Mientras confía en sus clientes y liderazgo para su
experiencia en la industria, debe estar en un rol para proporcionar conocimiento técnico y articular
lo que es factible. Veamos algunas habilidades duras y blandas que probablemente necesitará.
Dominio de SQL
SQL, también llamado lenguaje de consulta estructurado, es un lenguaje de consulta para
recuperar, transformar y escribir datos de tablas. Una base de datos relacional es la forma más
común de organizar datos, almacenándolos en tablas que están conectadas entre sí de forma muy
similar a BUSCARV en Excel. Las plataformas de bases de datos relacionales como MySQL,
Microsoft SQL Server, Oracle, SQLite y PostgreSQL admiten SQL. Como puede notar, SQL y las
bases de datos relacionales están tan estrechamente acopladas que "SQL" se usa a menudo en la
marca de la base de datos relacional, como "MySQL" y "Microsoft SQL Server".
El ejemplo 8-1 es una consulta SQL simple que recupera los campos CLIENTE_ID y NOMBRE de
una tabla CLIENTE, para registros que están en el ESTADO de 'TX'.
Ejemplo 8-1. Una consulta SQL simple
SELECT CUSTOMER_ID, NAME
FROM CUSTOMER
WHERE STATE = 'TX'
En pocas palabras, es difícil llegar a ningún lado como profesional de la ciencia de datos sin
competencia en SQL. Las empresas utilizan almacenes de datos y SQL es casi siempre el medio
para recuperar los datos. SELECT, WHERE, GROUP BY, ORDER BY, CASE, INNER JOIN y LEFT
JOIN deberían ser palabras clave familiares de SQL. Es aún mejor conocer las subconsultas, las
tablas derivadas, las expresiones de tablas comunes y las funciones de ventanas para obtener la
mayor utilidad de sus datos.
186
puedo crear una consulta SQL extrayendo todos los registros de la tabla CLIENTE y colocarlos en
un marco de datos de Pandas.
Ejemplo 8-3. Importación de una consulta SQL en un Pandas DataFrame
from sqlalchemy import create_engine, text
import pandas as pd
engine = create_engine('sqlite:///thunderbird_manufacturing.db')
conn = engine.connect()
df = pd.read_sql("SELECT * FROM CUSTOMER", conn)
print(df) # prints SQL results as DataFrame
SQL se usó aquí para cerrar la brecha entre la base de datos relacional y mi entorno de Python, y
cargar los datos en un Pandas DataFrame. Si tengo cálculos exigentes que SQL está equipado
para manejar, es más eficiente que lo haga en el servidor de la base de datos usando SQL en lugar
de localmente en mi computadora usando Pandas. En pocas palabras, Pandas y SQL pueden
trabajar juntos y no son tecnologías competidoras.
Lo mismo ocurre con NoSQL, que incluye plataformas como Couchbase y MongoDB. Si bien
algunos lectores pueden estar en desacuerdo y presentar argumentos válidos, creo que comparar
NoSQL con SQL es comparar manzanas y naranjas. Sí, ambos almacenan datos y brindan
capacidades de consulta, pero no creo que eso los ponga en competencia. Tienen diferentes
calidades para diferentes casos de uso. NoSQL significa "no solo SQL" y está mejor equipado para
almacenar datos no estructurados, como imágenes o artículos de texto de formato libre. SQL está
mejor equipado para almacenar datos estructurados. SQL mantiene la integridad de los datos de
manera más agresiva que NoSQL, aunque a costa de la sobrecarga informática y menos
escalabilidad.
Competencia en programación
Por lo general, muchos científicos de datos no tienen la habilidad de codificar, al menos no al nivel
de un ingeniero de software. Sin embargo, cada vez es más importante que codifiquen. Esto brinda
la oportunidad de obtener una ventaja. Aprenda programación orientada a objetos, programación
funcional, pruebas unitarias, control de versiones (p. ej., Git y GitHub), análisis de algoritmos Big-O,
criptografía y otros conceptos informáticos relevantes y características del lenguaje que encuentre.
Aquí está el por qué. Supongamos que creó un modelo de regresión prometedor, como una
regresión logística o una red neuronal, en función de algunos datos de muestra que le dieron. Pide
a los programadores internos de su departamento de TI que lo "conecten" a una pieza de software
existente.
Miran tu lanzamiento con cautela. “Necesitamos reescribir esto en Java, no en Python”, dicen a
regañadientes. "¿Dónde están sus pruebas unitarias?" otro pregunta. “¿No tienes ninguna clase o
tipo definido? Tenemos que rediseñar este código para que esté orientado a objetos”. Además de
eso, no entienden las matemáticas de su modelo y, con razón, se preocupan de que se comporte
mal con los datos que no ha visto antes. Dado que no ha definido pruebas unitarias, lo que no es
fácil de hacer con el aprendizaje automático, no están seguros de cómo verificar la calidad de su
modelo. También preguntan cómo se van a gestionar dos versiones de código (Python y Java)?
Empiezas a sentirte fuera de tu elemento y dices: "No entiendo por qué el script de Python no se
puede simplemente conectar". Uno de ellos hace una pausa contemplativa y responde: “Podríamos
187
crear un servicio web con Flask y evitar tener que volver a escribir en Java. Sin embargo, las otras
preocupaciones no desaparecen. Entonces tenemos que preocuparnos por la escalabilidad y el alto
tráfico que llega al servicio web. Espere.. tal vez podamos implementar en la nube de Microsoft
Azure como un conjunto de escalado de máquinas virtuales, pero aún tenemos que diseñar los
backends. Mira, esto tiene que ser rediseñado sin importar cómo lo enfoques”.
Esta es precisamente la razón por la que muchos científicos de datos tienen un trabajo que nunca
sale de su computadora portátil. De hecho, poner el aprendizaje automático en producción se ha
vuelto tan difícil de lograr que se ha convertido en un tema candente en los últimos años. Existe
una brecha enorme entre los científicos de datos y los ingenieros de software, por lo que,
naturalmente, existe una presión para que los profesionales de la ciencia de datos se conviertan
ahora en ingenieros de software.
Esto puede sonar abrumador ya que la ciencia de datos ya tiene un alcance sobrecargado, con
muchas disciplinas y requisitos. Sin embargo, esto no es para demostrar que necesita aprender
Java. Puede ser un ingeniero de software efectivo en Python (o cualquier lenguaje que prefiera),
pero debe ser bueno en eso. Aprenda programación orientada a objetos, estructuras de datos,
programación funcional, concurrencia y otros patrones de diseño. Dos buenos libros para abordar
estos temas para Python incluyen Fluent Python, 2nd Ed. de Luciano Ramalho (O'Reilly) y Beyond
the Basic Stuff with Python de Al Sweigart (No Starch).
188
enloquecedora de aprender Python, ya que estas trampas técnicas no son obvias para los recién
llegados. También es una forma de encontrar y presentar un hallazgo, solo para descubrir que fue
el resultado de un error y, por lo tanto, demasiado bueno para ser verdad.
No estoy abogando por que evites los cuadernos. Por todos los medios, ¡úsalos si te hacen feliz a ti
y a tu lugar de trabajo! Sin embargo, estoy abogando por no confiar en ellos. Joel Grus, autor de
Data Science from Scratch (O'Reilly), dio una charla en JupyterCon sobre este mismo tema que
puedes ver aquí.
SESGO DE ANCLAJE Y PRIMEROS LENGUAJES DE PROGRAMACIÓN
Es común que los profesionales técnicos se involucren emocionalmente con las tecnologías y las
plataformas, especialmente los lenguajes de programación. ¡Por favor, no hagas esto! Este tipo de
tribalismo no es productivo e ignora la realidad de que cada lenguaje de programación se adapta a
diferentes cualidades y casos de uso. Otra realidad es que algunos lenguajes de programación
prenden y otros no, a menudo por razones que no tienen nada que ver con los méritos en el diseño
de lenguajes. Si una gran empresa no paga por su apoyo, sus posibilidades de supervivencia son
escasas.
Hablamos sobre diferentes tipos de sesgos cognitivos en el Capítulo 3. Otro es el sesgo de anclaje,
que establece que podemos volvernos parciales con lo primero que aprendemos, como un lenguaje
de programación. Si te sientes obligado a aprender un nuevo idioma, ¡sé abierto y dale una
oportunidad! Ningún idioma es perfecto, y lo único que importa es que haga el trabajo.
Sin embargo, tenga cuidado si el soporte del idioma es cuestionable porque está en soporte vital,
no recibe actualizaciones o carece de un mantenedor corporativo. Ejemplos de esto incluyen VBA
de Microsoft, Ceylon de Red Hat y Haskell.
Visualización de datos
Otra habilidad técnica en la que debe tener cierto grado de competencia es la visualización de
datos. Siéntase cómodo haciendo tablas, gráficos y diagramas que no solo le cuenten historias a la
gerencia, sino que también ayuden a sus propios esfuerzos de exploración de datos. Puede
resumir los datos con un comando SQL, pero a veces un gráfico de barras o un diagrama de
dispersión le dará una mejor idea de sus datos en menos tiempo.
Cuando se trata de qué herramientas usar para la visualización de datos, esto es mucho más difícil
de responder porque hay mucha fragmentación y elección. Si trabaja en un entorno de oficina
tradicional, Excel y PowerPoint suelen ser las herramientas de visualización preferidas, ¿y sabe
qué? ¡Están bastante bien! No los uso para todo pero cumplen la gran mayoría de tareas.
¿Necesita un diagrama de dispersión en un conjunto de datos pequeño o mediano? ¿O un
histograma? ¡No hay problema! Puede tener uno integrado unos minutos después de copiar/pegar
sus datos en un libro de Excel. Esto es excelente para visualizaciones de gráficos de una sola vez,
y no hay que avergonzarse de usar Excel cuando funciona.
Sin embargo, hay situaciones en las que es posible que desee programar la creación de gráficos
para que sea repetible y reutilizable o se integre con su código Python. matplotlib ha sido el recurso
durante algún tiempo, y es difícil evitarlo cuando Python es su plataforma. Seaborn proporciona un
contenedor en la parte superior de matplotlib para que sea más fácil de usar para los tipos de
gráficos comunes. SymPy, que usamos mucho a lo largo de este libro, usa matplotlib como
backend. Sin embargo, algunos consideran que matplotlib es tan maduro que se acerca al estado
heredado. Las bibliotecas como Plotly han ido en aumento y son agradables de usar, y se basan en
la biblioteca JavaScript D3.js. Personalmente, estoy teniendo éxito con Manim. Las visualizaciones
de estilo 3Blue1Brown que produce son extraordinarias y tienen ese "¡Guau!" factor con los
clientes, y la API es sorprendentemente fácil de usar teniendo en cuenta el poder de animación que
189
tiene. Sin embargo, es una biblioteca joven y aún no ha alcanzado la madurez, lo que significa que
es posible que tenga cambios importantes en el código a medida que evoluciona con cada versión.
No puede equivocarse al explorar todas estas soluciones, y si su empleador/cliente no tiene una
preferencia, puede encontrar la que mejor se adapte a sus necesidades.
Hay plataformas comerciales con licencia como Tableau, que están bien hasta cierto punto. Se
propusieron crear un software patentado que se especialice en visualización y crear una interfaz de
arrastrar y soltar para que sea accesible para usuarios no técnicos. Tableau incluso tiene un
documento técnico titulado "Convierta a todos en su organización en científicos de datos", que no
ayuda con el problema de definición de científico de datos mencionado anteriormente. Los desafíos
que encontré con Tableau es que solo hace una buena visualización y requiere una licencia
considerable. Si bien puede integrar Python con TabPy de alguna manera, puede elegir usar las
bibliotecas de código abierto compatibles mencionadas anteriormente, a menos que su empleador
quiera usar Tableau.
Conociendo su industria
Comparemos dos industrias: transmisión de películas (p. ej., Netflix) y defensa aeroespacial (p. ej.,
Lockheed Martin). ¿Tienen algo en común? ¡Difícilmente! Ambas son empresas impulsadas por la
tecnología, pero una transmite películas para los consumidores y la otra construye aviones con
artillería.
Cuando aconsejo sobre inteligencia artificial y seguridad del sistema, una de las primeras cosas
que señalo es que estas dos industrias tienen tolerancias de riesgo muy diferentes. Una empresa
de transmisión de películas puede promocionar que tiene un sistema de inteligencia artificial que
aprende qué películas recomendar a los consumidores, pero ¿qué tan catastrófico es cuando da
una mala recomendación? Bueno, en el peor de los casos, tiene un consumidor levemente irritado
que perdió dos horas viendo una película que no disfrutó.
Pero, ¿qué pasa con la compañía de defensa aeroespacial? Si un avión de combate tiene IA a
bordo que dispara automáticamente a los objetivos, ¿qué tan catastrófico sería si estuviera mal?
¡Estamos hablando de vidas humanas ahora, no de recomendaciones de películas!
La brecha de tolerancia al riesgo entre ambas industrias es amplia. Naturalmente, la compañía de
defensa aeroespacial será mucho más conservadora al implementar cualquier sistema
experimental. Esto significa que los grupos de trabajo burocráticos y de seguridad evalúan y
detienen cualquier proyecto que consideren de alto riesgo inaceptable, y con razón. Sin embargo,
lo que es interesante es el éxito de la IA en las nuevas empresas de Silicon Valley, la mayoría en
aplicaciones de bajo riesgo como recomendaciones de películas, han creado FOMO ("miedo a
perderse") con los ejecutivos y el liderazgo de la industria de defensa. Esto probablemente se deba
a que la disparidad en la tolerancia al riesgo entre estos dos dominios no se destaca lo suficiente.
190
Por supuesto, existe un amplio espectro de gravedad del riesgo entre estas dos industrias, entre
"usuario irritado" y "pérdida de vidas humanas". Los bancos pueden usar IA para determinar quién
calificará para préstamos, pero eso conlleva riesgos al discriminar ciertos datos demográficos. Los
sistemas de justicia penal han experimentado con IA en los sistemas de libertad condicional y
vigilancia, solo para encontrarse con los mismos problemas de discriminación. Las redes sociales
pueden usar IA para determinar qué publicaciones de usuarios son aceptables, pero enojan a sus
usuarios cuando suprimen contenido "inofensivo" (falses positivos), así como a los legisladores
cuando no suprimen contenido "dañino" (falses negativos).
Esto demuestra la necesidad de conocer su industria. Si desea hacer mucho aprendizaje
automático, probablemente querrá trabajar en industrias de bajo riesgo donde los falses positivos y
los falses negativos no pongan en peligro ni molesten a nadie. Pero si nada de eso le atrae y desea
trabajar en aplicaciones más audaces, como automóviles autónomos, aviación y medicina, espere
que sus modelos de aprendizaje automático sean rechazados con frecuencia.
En estas industrias de mayor riesgo, no se sorprenda si se requieren doctorados específicos y otras
credenciales formales. Incluso con un doctorado especializado, los falses positivos y los falses
negativos no desaparecen por arte de magia. Si no desea seguir una especialización tan
comprometida, probablemente será mejor que aprenda otras herramientas además del aprendizaje
automático, incluida la ingeniería de software, la optimización, las estadísticas y los
sistemas/heurística de reglas comerciales.
Aprendizaje Productivo
En un especial de stand-up de 2008, el comediante Brian Regan comparó su falta de curiosidad
con la de aquellos que leen periódicos. Al señalar que una historia de primera plana nunca
concluye, afirmó que no tiene ningún deseo de pasar a la página especificada para saber cómo
termina. "Y después de un juicio de nueve años, el jurado finalmente llegó con un veredicto de
continúa en la página 22 en la columna C.. Supongo que nunca lo sabré", bromea con desdén.
Luego contrasta con aquellos que pasan las páginas, exclamando: “¡Quiero aprender! ¡Quiero ser
un aprendiz de cosas!”
Si bien Brian Regan podría haber tenido la intención de ser autocrítico, tal vez tenía razón en algo.
Aprender un tema por el simple hecho de hacerlo difícilmente es una motivación, y ser
desinteresado no siempre es algo malo. Si elige un libro de texto de cálculo y no tiene ningún
propósito para aprenderlo, probablemente terminará desanimado y frustrado. Necesita tener un
proyecto u objetivo en mente, y si encuentra un tema poco interesante, ¿por qué molestarse en
aprenderlo? Personalmente, cuando me permití perder interés en temas que no encontraba
relevantes, fue increíblemente liberador. Aún más sorprendente, mi productividad se disparó.
No significa que debas ser poco curioso. Sin embargo, hay mucha información disponible y priorizar
lo que aprendes es una habilidad invaluable. Puede hacer preguntas sobre por qué algo es útil y, si
no puede obtener una respuesta directa, ¡permítase seguir adelante! ¿Está todo el mundo
hablando del procesamiento del lenguaje natural? ¡No significa que tengas que hacerlo! De todos
modos, la mayoría de las empresas no necesitan el procesamiento del lenguaje natural, por lo que
está bien decir que no vale la pena su esfuerzo o tiempo.
Ya sea que tenga proyectos en su trabajo o cree los suyos propios para el autoaprendizaje, tenga
algo tangible en lo que trabajar. Solo usted puede decidir qué vale la pena aprender, y puede
abandonar el FOMO en busca de cosas que le parezcan interesantes y relevantes.
191
de la organización y la industria. Una queja común que escuché de los gerentes es que su científico
de datos quiere trabajar en problemas que encuentran interesantes pero que no agregan valor a la
organización. También he escuchado quejas de practicantes que quieren exposición y movilidad
ascendente pero se sienten apartados y escondidos en su organización.
Es cierto que un asesor tiene un trabajo más cómodo en algunos aspectos. Brindan asesoramiento
e información a los gerentes y ayudan a proporcionar una dirección estratégica a una empresa. Por
lo general, no son los que escriben el código o rastrean los datos, pero ayudan a la gerencia a
contratar a las personas que lo hacen. El riesgo de carrera es diferente, ya que no se preocupan
por cumplir con los plazos de sprint, lidiar con errores de código o modelos que se comportan mal
como lo hacen los profesionales. Pero sí tienen que preocuparse por mantenerse informados,
creíbles y relevantes.
Para ser un asesor eficaz, debe ser realmenteconocedores y saben cosas que otras personas no
saben. Tiene que ser información crítica y relevante que se ajuste a las necesidades de su cliente.
Para mantenerse relevante, debe leer, leer y leer más todos los días.. buscando y sintetizando
información que otros pasan por alto. No es suficiente estar familiarizado con el aprendizaje
automático, las estadísticas y el aprendizaje profundo. Debe prestar atención a la industria de su
cliente, así como a otras industrias, rastreando quién está teniendo éxito y quién no. También debe
aprender a combinar la solución correcta con el problema correcto, en un panorama empresarial en
el que muchos buscan una bala de plata. Y para hacer todo esto, debe ser un comunicador efectivo
y compartir información de una manera que ayude a su cliente, no solo demostrar lo que sabe.
El mayor riesgo para un asesor es brindar información que termine siendo incorrecta. Algunos
consultores son bastante efectivos para redirigir la culpa a factores externos, como “nadie en la
industria vio venir esto” o “¡este es un evento de seis sigma!” lo que significa que un evento
indeseable tenía una posibilidad entre quinientos millones de ocurrir, pero lo hizo de todos modos.
Otro riesgo es no tener las habilidades duras de un profesional y desconectarse del aspecto técnico
del negocio. Es por eso que es una buena idea practicar regularmente la codificación y el modelado
en casa, o al menos hacer que los libros técnicos formen parte de su lectura.
Al final, un buen asesor trata de ser un puente entre el cliente y su objetivo final, a menudo llenando
un enorme vacío de conocimiento que existe. No se trata de facturar el número máximo de horas e
inventar trabajo pesado, sino de identificar realmente lo que le preocupa a su cliente y ayudarlo a
dormir por la noche.
Cuando los proyectos se planifican sobre herramientas en lugar de problemas, existe una alta
probabilidad de que el proyecto no tenga éxito. Esto significa que, como asesor, debe perfeccionar
192
sus habilidades de escucha e identificar las preguntas que los clientes tienen dificultades para
hacer, y mucho menos tener las respuestas. Si una importante cadena de comida rápida lo ha
contratado para ayudar con la "estrategia de inteligencia artificial" y ve que su departamento de
recursos humanos se apresura a contratar talento de aprendizaje profundo, es su trabajo preguntar:
"¿Qué problemas con el aprendizaje profundo está tratando de resolver?" Si no puede obtener una
respuesta clara, desea alentar a la gerencia a dar un paso atrás y evaluar qué problemas reales
están enfrentando como industria. ¿Están teniendo ineficiencia en la programación del personal?
Bueno, no necesitan un aprendizaje profundo. ¡Necesitan programación lineal! Esto puede parecer
básico para algunos lectores, pero hoy en día mucha gerencia se esfuerza por hacer estas
distinciones. Más de una vez, me he encontrado con proveedores y consultores que califican sus
soluciones de programación lineal como IA, que luego pueden combinarse semánticamente con el
aprendizaje profundo.
Definición de roles
Digamos que lo contratan como científico de datos. La entrevista fue genial. Hiciste preguntas
sobre el papel y obtuviste respuestas directas. Le ofrecen un trabajo y, lo más importante, debe
saber en qué proyectos trabajará.
Siempre quiere entrar en un rol que esté claramente definido y tenga objetivos tangibles. No se
puede adivinar en qué se supone que debes estar trabajando. Aún mejor, debe tener un liderazgo
con una visión clara que entienda lo que necesita el negocio. Te conviertes en el ejecutor de
objetivos claramente definidos y conoces a tu cliente.
Por el contrario, si lo contrataron para un puesto porque el departamento quiere estar "basado en
datos" o tener una ventaja competitiva en "ciencia de datos", esta es una señal de alerta. Lo más
probable es que tenga la carga de encontrar problemas para resolver y vender cualquier fruta que
encuentre. Cuando solicita orientación estratégica, se le dice que aplique el "aprendizaje
automático" al negocio. Pero claro, cuando lo único que tienes es un martillo, todo empieza a
parecer un clavo. Los equipos de ciencia de datos se sienten presionados para tener una solución
(p. ej., aprendizaje automático) incluso antes de tener un objetivo o un problema que resolver. Una
193
vez que se encuentra un problema, resulta difícil obtener la participación de las partes interesadas
y alinear los recursos, y el enfoque comienza a rebotar de una fruta al alcance de la mano a otra.
El problema aquí es que lo contrataron para un rol basado en una palabra de moda, no en una
función. La mala definición de roles tiende a propagarse a otros problemas que se analizan a
continuación. Pasemos al enfoque organizacional.
194
experiencia de tiempo completo para interpretar los datos, y requiere su conocimiento del dominio.
¡Después de todo, sus datos son su negocio! Y si está solicitando acceso a sus datos, está
solicitando entrar en su negocio.
Además de eso, los científicos de datos pueden sobrestimar su capacidad para interpretar
conjuntos de datos extranjeros y la experiencia de dominio necesaria para usarlos. Para superar
este obstáculo, debe desarrollar la confianza y la aceptación de cada socio con experiencia,
negociar una transferencia de conocimientos y, si es necesario, otorgarles un papel importante en
el proyecto.
Recursos adecuados
Otro riesgo a tener en cuenta es no obtener los recursos adecuados para hacer su trabajo. Es difícil
ser arrojado a un rol y no tener lo que necesitas. Por supuesto, ser rudimentario e ingenioso es un
rasgo invaluable. Pero incluso el ingeniero de software/científico de datos más rudimentario puede
encontrarse rápidamente en un aprieto. A veces necesita cosas que cuestan dinero y su empleador
no puede presupuestarlas.
Supongamos que necesita una base de datos para realizar el trabajo de previsión. Tiene una
conexión deficiente a una base de datos de terceros, con tiempo de inactividad y desconexiones
frecuentes. Lo último que quiere escuchar es "hacer que funcione", pero esa es su situación.
Contempla replicar la base de datos localmente, pero para hacerlo, necesita almacenar 40 GB por
día y, por lo tanto, necesita un servidor o una instancia en la nube. ¡Ahora está claramente por
encima de su cabeza, un científico de datos que se convierte en un departamento de TI sin un
presupuesto de TI!
En estas situaciones, debe contemplar cómo puede tomar atajos sin dañar su proyecto. ¿Puede
retener solo los datos más recientes y eliminar el resto? ¿Puede crear una secuencia de comandos
de Python para el manejo de errores que se vuelva a conectar cuando ocurra una desconexión,
mientras divide los datos en lotes para que se recupere del último lote exitoso?
Si este problema/solución suena específico, sí, tuve que hacer esto, y sí, ¡funcionó! Es satisfactorio
encontrar soluciones alternativas y agilizar un proceso sin incurrir en más costos. Pero,
inevitablemente, para muchos proyectos de datos es posible que necesite canalizaciones de datos,
servidores, clústeres, estaciones de trabajo basadas en GPU y otros recursos computacionales que
una computadora de escritorio no puede ofrecer. En otras palabras, estas cosas pueden costar
dinero y es posible que su organización no pueda presupuestarlas.
Objetivos razonables
Este es uno grande a tener en cuenta. En un paisaje lleno de exageraciones y promesas
disparatadas, es fácil encontrar objetivos irrazonables.
Hay situaciones en las que un gerente contrata a un científico de datos y espera que entre sin
esfuerzo y agregue valor exponencial a la organización. Esto ciertamente puede suceder si la
organización todavía está haciendo trabajo manual y las oportunidades para automatizar están en
todas partes. Por ejemplo, si la organización está haciendo todo su trabajo en hojas de cálculo y los
pronósticos se hacen con puras conjeturas, esta es una gran oportunidad para que un profesional
de la ciencia de datos optimice los procesos en una base de datos y avance incluso con modelos
de regresión simples.
Por otro lado, si la organización contrata a un científico de datos para implementar el aprendizaje
automático en su software que reconoce específicamente objetos en imágenes, esto es mucho más
difícil. ¡Un científico de datos bien informado tiene que explicarle a la gerencia que este es un
esfuerzo que costará al menos cientos de miles de dólares! No solo hay que recopilar imágenes,
sino que se debe contratar mano de obra para etiquetar los objetos en las imágenes. ¡Y eso es solo
recopilar los datos!
195
Es común que un científico de datos pase sus primeros 18 meses explicando a la gerencia por qué
no ha cumplido, porque todavía está tratando de recopilar y limpiar datos, que es el 95 % de los
esfuerzos de aprendizaje automático. La gerencia puede desilusionarse con esto porque fueron
víctimas de una narrativa popular de que el aprendizaje automático y la IA eliminarían los procesos
manuales, solo para descubrir que cambiaron un conjunto de procesos manuales por otro: la
adquisición de datos etiquetados.
Por lo tanto, tenga cuidado con los entornos que establecen objetivos poco razonables y encuentre
formas diplomáticas de manejar las expectativas con la gerencia, especialmente cuando otros les
prometieron un "botón FÁCIL". Hay muchas afirmaciones de revistas de negocios de buena
reputación y consultorías de gestión de alto valor de que la IA superinteligente está a la vuelta de la
esquina. Los gerentes que carecen de experiencia técnica pueden ser víctimas de esta narrativa
exagerada.
¿CUI BONO?
La expresión latina cui bono significa “¿quién se beneficia?” Es una buena pregunta para hacer
cuando se encuentra tratando de dar sentido a los comportamientos cuando el efecto Jabberwocky
está en pleno apogeo. Cuando los medios promueven historias sobre inteligencia artificial, ¿quién
se beneficia de eso? Independientemente de su respuesta, los medios también se benefician de los
clics y los ingresos publicitarios. Las consultorías de gestión de alto valor crean más horas
facturables en torno a la "estrategia de IA". Un fabricante de chips puede promover el aprendizaje
profundo para vender más tarjetas gráficas, y las plataformas en la nube pueden vender más
almacenamiento de datos y tiempo de CPU para proyectos de aprendizaje automático.
¿Qué tienen en común todos estos partidos? No es solo que estén utilizando la IA como un medio
para vender sus productos, sino que no tienen ningún interés a largo plazo en el éxito de sus
clientes. Están vendiendo unidades, no el resultado de un proyecto, muy parecido a vender palas
durante la fiebre del oro.
Sin embargo, no estoy diciendo que estas motivaciones de los medios y los proveedores no sean
éticas. Es el trabajo de sus empleados ganar dinero para su empresa y mantener a sus familias.
Las afirmaciones que promocionan su producto pueden incluso ser legítimas y alcanzables. Sin
embargo, no se puede descartar que una vez que se promueve un reclamo, es difícil dar marcha
atrás, incluso cuando se reconoce que es inalcanzable. Muchas empresas podrían girar y redirigir
sus esfuerzos en lugar de admitir que sus afirmaciones no dieron resultado. Así que tenga en
cuenta esta dinámica y siempre pregunte "¿cui bono?"
196
¿Se puede contratar a un científico de datos para un puesto que no produzca ningún valor, a pesar
del buen trabajo y los esfuerzos diligentes? Sí. Los factores fuera del control de uno pueden anular
incluso el mejor trabajo, y es importante estar atento a esto.
La película de comedia de Mike Judge de 1999 Office Space se convirtió en un clásico de culto
para muchos trabajadores corporativos en los Estados Unidos. En la película, el trabajador de TI y
protagonista Peter Gibbons sale mentalmente de un trabajo de codificación que reporta a ocho
gerentes diferentes. Cuando los consultores de reducción de personal le preguntan qué hace
durante el día, responde honestamente que hace "alrededor de 15 minutos de trabajo real". No
estropearé el resto de la película para lectores no iniciados, pero como cualquier gran comedia, el
resultado probablemente no sea el esperado.
Para profundizar en el ejemplo anterior, reemplazar un sistema que no está roto es lo que el difunto
antropólogo David Graeber describiría como un trabajo de mierda (perdón por mi francés). Según
Graeber, un trabajo de mierda es un empleo pagado que es tan completamente inútil, innecesario o
pernicioso que incluso el empleado no puede justificar su existencia, sino que tiene que fingir lo
contrario. En su libro Bullshit Jobs y su artículo viral de 2013, Graeber teoriza que estos trabajos se
están volviendo tan comunes que están cobrando un precio psicológico en la fuerza laboral y la
economía.
Si bien el trabajo de Graeber está plagado de sesgos de autoselección y hallazgos anecdóticos, y
la falta de evidencia empírica es un objetivo discutible para la crítica, es difícil afirmar que estos
trabajos de mierda no existen. En defensa de Graeber, esto es empíricamente difícil de medir y
pocos trabajadores responderán honestamente a una encuesta por temor a poner en peligro sus
propias carreras.
¿Están las carreras de ciencia de datos a salvo de estos problemas? Cassie Kozykrov, directora
científica de decisiones de Google, comparte una extraña anécdota que ayuda a responder esta
pregunta:
Hace varios años, un amigo director de ingeniería que trabaja en tecnología se lamentaba de sus
inútiles científicos de datos. “Creo que podría estar contratando científicos de datos de la misma
manera que un capo de la droga compra un tigre para su patio trasero”, le dije. “No sabes lo que
quieres con el tigre, pero todos los demás capos de la droga tienen uno”.
¡Ay! ¿Es posible que la gerencia contrate científicos de datos para aumentar la credibilidad
percibida y la posición corporativa de una organización? Si se encuentra en un trabajo que no está
diseñado para crear valor, piense estratégicamente cómo puede influir en un cambio positivo.
¿Puedes crear oportunidades en lugar de esperar a que otros te las proporcionen? ¿Puede tomar
posesión de manera significativa de las iniciativas que identifique y, por lo tanto, hacer crecer su
carrera? Si esto no es posible, empoderarse para buscar mejores oportunidades.
197
¿QUÉ ES SHADOW IT?
La tecnología de la información en la sombra (TI en la sombra) es un término que describe a los
trabajadores de oficina que crean sistemas fuera de su departamento de TI. Estos sistemas pueden
incluir bases de datos, scripts y procesos, así como software creado por proveedores y empleados
sin la participación del departamento de TI.
Shadow IT solía estar mal visto en las organizaciones, ya que no está regulado y opera fuera del
alcance de control del departamento de TI. Cuando los departamentos de finanzas, marketing y
otros departamentos que no son de TI deciden manipular sus propias operaciones de TI
deshonestas, ciertamente pueden crear costos ocultos para la organización debido a problemas de
ineficiencia y seguridad. Pueden surgir políticas desagradables cuando los departamentos de TI y
los departamentos que no son de TI chocan, acusándose mutuamente de no permanecer en su
carril o simplemente cooptando roles para la seguridad laboral.
Sin embargo, un beneficio del movimiento de la ciencia de datos es que ha hecho que la TI en la
sombra sea más aceptada como una necesidad para la innovación. Las personas que no son de TI
pueden crear prototipos y experimentar con la creación de conjuntos de datos, scripts de Python y
herramientas de regresión. Luego, el departamento de TI puede recoger estas innovaciones y
respaldarlas formalmente a medida que maduran. Esto también permite que el negocio se vuelva
más ágil. Un cambio de regla comercial puede ser una edición rápida de un script de Python o una
base de datos interna, en lugar de presentar un ticket con la mesa de ayuda de TI. Si ese cambio
debe estar sujeto a pruebas rigurosas y trámites burocráticos es ciertamente una compensación de
costos por la capacidad de respuesta.
En general, si se encuentra en una función de TI en la sombra (lo cual es probable), asegúrese de
comprender los riesgos y sea amable con el departamento de TI. Si tiene éxito, puede ser
fortalecedor y gratificante apoyar su negocio de esta manera. Si siente un posible conflicto con su
departamento de TI, sea sincero con su liderazgo y hágaselo saber. Si puede decir con sinceridad
que su trabajo es "creación de prototipos" y "exploración", entonces su liderazgo puede argumentar
que está fuera del ámbito de TI. Sin embargo, nunca se vuelva rebelde sin el apoyo de su liderazgo
y déjelos lidiar con la política interdepartamental.
198
estandarizada con un alcance restringido. Si aprendimos algo de los últimos 10 años observando el
movimiento de la ciencia de datos, la definición importa. Un científico de datos está evolucionando
para convertirse en un ingeniero de software con competencia en estadísticas, optimización y
aprendizaje automático. Es posible que un científico de datos ya ni siquiera tenga ese título de
científico de datos. Si bien este es un conjunto de requisitos mucho más amplio que cuando el
científico de datos fue declarado el "trabajo más sexy del siglo XXI", se está volviendo necesario
tener estas habilidades.
Otra opción es especializarse en favor de títulos más enfocados, y esto viene ocurriendo cada vez
más durante los últimos años. Los roles como ingeniero de visión por computadora, ingeniero de
datos, analista de datos, investigador, analista de investigación de operaciones y asesor/consultor
están regresando. Cada vez vemos menos roles de científicos de datos, y es probable que esta
tendencia continúe en los próximos 10 años, principalmente debido a la especialización de roles.
Sin duda es una opción para seguir esta tendencia.
Es fundamental tener en cuenta que el mercado laboral ha cambiado drásticamente, y es por eso
que necesita las ventajas competitivas que se enumeran en este capítulo. Mientras que los
científicos de datos eran vistos como unicornios en 2014 con salarios de seis cifras, hoy en día un
trabajo de científico de datos en cualquier empresa puede recibir fácilmente cientos o miles de
aplicaciones y solo ofrece un salario de cinco cifras. Los títulos y bootcamps de ciencia de datos
han creado un enorme auge en la oferta de profesionales de ciencia de datos. Por lo tanto, son
trabajos de aterrizaje competitivos comercializados como científico de datos o ciencia de datos en
general. ¡Es por eso que buscar roles como analista, investigación de operaciones y desarrollador
de software no es necesariamente una mala idea! Vicki Boykis, ingeniera de aprendizaje
automático en Tumblr, probablemente lo dice mejor en su artículo de blog "La ciencia de datos es
diferente ahora" :
Recuerde que el objetivo final.. es vencer a las hordas haciendo títulos de ciencia de datos,
bootcamps y trabajando a través de tutoriales.
Desea poner su pie en la puerta, obtener una posición adyacente a los datos y avanzar hacia el
trabajo de sus sueños, mientras descubre todo lo que pueda sobre la industria de la tecnología en
general.
No te dejes paralizar por el análisis. Escoge un pedazo pequeño de algo y comienza allí. Haz algo
pequeño. Aprende algo pequeño, construye algo pequeño. Dile a otras personas. Recuerde que su
primer trabajo en ciencia de datos probablemente no sea como científico de datos.
Conclusión
Este fue un capítulo diferente del resto de este libro, pero es importante si desea navegar por el
panorama laboral de la ciencia de datos y aplicar de manera efectiva el conocimiento de este libro.
Puede ser desconcertante aprender herramientas estadísticas y aprendizaje automático solo para
descubrir que gran parte del panorama laboral lo llevará a otro trabajo. Cuando esto suceda,
aproveche la oportunidad para seguir aprendiendo y adquiriendo habilidades. Cuando integre su
conocimiento matemático esencial con un dominio de la programación y la ingeniería de software,
su valor se multiplicará por diez solo por tener una comprensión de la brecha de TI y ciencia de
datos.
Recuerde evitar la exageración en favor de soluciones prácticas, y no se deje atrapar tanto por el
ámbito técnico que las fuerzas del mercado lo tomen por sorpresa. Comprender las motivaciones
de gestión y liderazgo, así como las motivaciones de las personas en general. Comprender por qué
funcionan las cosas, no solo cómo funcionan. Sea curioso por qué una técnica o herramienta
resuelve un problema, no solo cómo operan los aspectos técnicos de la misma.
No aprenda porque sí, sino para desarrollar capacidades y emparejar la herramienta correcta con el
problema correcto. Una de las formas más efectivas de aprender es elegir un problema (¡no una
herramienta!) que le parezca interesante. Tirar de ese hilo lleva a otra cosa por la que sentir
curiosidad, y luego a otra, y luego a otra. Tienes un objetivo en mente, así que sigue yendo por las
madrigueras correctas y sabe cuándo sacarte de los demás. Es absolutamente gratificante adoptar
este enfoque, y es sorprendente la cantidad de experiencia que puede obtener en un corto período
de tiempo.
199
Apéndice A. Temas complementarios
Uso de renderizado LaTeX con SymPy
A medida que se sienta más cómodo con la notación matemática, puede ser útil tomar sus
expresiones SymPy y mostrarlas en notación matemática.
La forma más rápida de hacer esto es usar la función latex() en SymPy en su expresión y luego
copiar el resultado a un visor matemático LaTeX.
El ejemplo A-1 es un ejemplo que toma una expresión simple y la convierte en una cadena LaTeX.
Por supuesto, podemos tomar los resultados de derivadas, integrales y otras operaciones de
SymPy y convertirlas también en LaTeX. Pero mantengamos el ejemplo sencillo.
Ejemplo A-1. Usando SymPy para convertir una expresión en LaTeX
from sympy import *
x,y = symbols('x y')
z = x**2 / sqrt(2*y**3 - 1)
print(latex(z))
# prints
# \frac{x^{2}}{\sqrt{2 y^{3} - 1}}
Esta cadena \frac{x^{2}}{\sqrt{2 y^{3} - 1}} tiene formato mathlatex, y hay una variedad de
herramientas y formatos de documentos que se pueden adaptar para admitirlo. Pero para
simplemente renderizar el mathlatex, vaya a un editor de ecuaciones de LaTeX. Aquí hay dos
diferentes que uso en línea:
Editor de ecuaciones LaTeX de Lagrida
Editor de ecuaciones CodeCogs
En la Figura A-1 utilizo el editor LaTeX de Lagrida para representar la expresión matemática.
Figura A-1. Usando un editor matemático para ver la salida de SymPy LaTeX
Si desea guardar el paso de copiar/pegar, puede agregar LaTeX directamente como un argumento
a la URL del editor CodeCogs LaTeX como se muestra en el Ejemplo A-2, y mostrará la ecuación
matemática renderizada en su navegador.
Ejemplo A-2. Abra una representación de Mathlatex usando CodeCogs
200
import webbrowser
from sympy import *
x,y = symbols('x y')
z = x**2 / sqrt(2*y**3 - 1)
webbrowser.open("https://latex.codecogs.com/png.image?\dpi{200}" + latex(z))
201
Tal como lo hicimos en el Capítulo 1, empaquetamos rectángulos debajo de la curva para el rango
que nos interesa, como se muestra en la Figura A-2.
Esto está usando solo seis rectángulos; obtendremos una mayor precisión si usáramos más
rectángulos. Implementemos beta_distribution() desde cero e integremos 1,000 rectángulos entre
0.9 y 1.0 como se muestra en el Ejemplo A-4.
Ejemplo A-4. Distribución beta desde cero
# Factorials multiply consecutive descending integers down to 1
# EXAMPLE: 5! = 5 * 4 * 3 * 2 * 1
def factorial(n: int):
f=1
for i in range(n):
f *= (i + 1)
return f
202
Notará que con la función beta_distribution(), proporcionamos una probabilidad x dada, un valor alfa
que cuantifica los éxitos y un valor beta que cuantifica los fracasos. La función devolverá la
probabilidad de que observemos una probabilidad x dada. Pero nuevamente, para obtener una
probabilidad de observar la probabilidad x, necesitamos encontrar un área dentro de un rango de
valores x.
Afortunadamente, tenemos nuestra función approach_integral() definida y lista para funcionar
desde el Capítulo 2. Podemos calcular la probabilidad de que la tasa de éxito sea superior al 90 %
y también inferior al 90 %, como se muestra en las últimas líneas.
Tenemos 65.000 bebedores de café y 500 pacientes con cáncer. Ahora bien, de esos 500
pacientes con cáncer, ¿cuántos son bebedores de café? Nos proporcionaron una probabilidad
condicional P(Coffee|Cancer) que podemos multiplicar contra esas 500 personas, lo que
debería darnos 425 pacientes con cáncer que beben café:
P(Coffee Drinker|Cancer) = .85
Coffee Drinkers with Cancer = 500 × .85 = 425
Ahora bien, ¿cuál es el porcentaje de bebedores de café que tienen cáncer? ¿Qué dos números
dividimos? Ya tenemos el número de personas que toman café y tienen cáncer. Por lo tanto,
proporcionamos eso contra el número total de bebedores de café:
Observe que la población N de 100 000 existe tanto en el numerador como en el denominador,
por lo que se cancela. ¿Esto te parece familiar ahora?
203
Efectivamente, ¡esto debería coincidir con el Teorema de Bayes!
Entonces, si se confunde con el teorema de Bayes o tiene problemas con la intuición que lo
respalda, intente tomar subconjuntos de una población fija en función de las probabilidades
proporcionadas. Luego puede rastrear su camino para voltear una probabilidad condicional.
El math.erf() se conoce como la función de error y se usa a menudo para calcular distribuciones
acumulativas. Finalmente, para hacer el CDF inverso desde cero, necesitará usar el inverso de la
función erf() llamada erfinv(). El ejemplo A-7 calcula mil pesos de golden retriever generados
aleatoriamente usando un CDF inverso codificado desde cero.
204
mean = 64.43
std_dev = 2.99
for i in range(0,1000):
random_p = random.uniform(0.0, 1.0)
print(inv_normal_cdf(random_p, mean, std_dev))
205
probabilidad en un período determinado y ver cómo esa probabilidad cambia en diferentes períodos
de tiempo.
Escalada de colinas y regresión lineal
Si encuentra que el cálculo es abrumador al construir el aprendizaje automático desde cero, puede
probar un método más de fuerza bruta. Probemos un algoritmo de escalada de colinas, donde
ajustamos aleatoriamente m y b agregando valores aleatorios para varias iteraciones. Estos valores
aleatorios serán positivos o negativos (lo que hará que la operación de suma sea efectivamente
resta), y solo nos quedaremos con los ajustes que mejoren nuestra suma de cuadrados.
Pero, ¿generamos cualquier número aleatorio como ajuste? Querremos preferir movimientos más
pequeños, pero ocasionalmente podemos permitir movimientos más grandes. De esta manera, en
su mayoría tenemos ajustes finos, pero ocasionalmente haremos grandes saltos si es necesario. La
mejor herramienta para hacer esto es una distribución normal estándar, con una media de 0 y una
desviación estándar de 1. Recuerde del Capítulo 3 que una distribución normal estándar tendrá una
alta densidad de valores cerca de 0, y cuanto más lejos esté el valor desde 0 (tanto en dirección
negativa como positiva), es menos probable que el valor se vuelva como se muestra en la Figura A-
4.
Figura A-4. La mayoría de los valores en una distribución normal estándar son pequeños y
cercanos a 0, mientras que los valores más grandes son menos frecuentes en las colas.
206
b_adjust = normal(0,1)
m += m_adjust
b += b_adjust
# Calculate loss, which is total sum squared error
new_loss = 0.0
for p in points:
new_loss += (p.y - (m * p.x + b)) ** 2
# If loss has improved, keep new values. Otherwise revert.
if new_loss < best_loss:
print("y = {0}x + {1}".format(m, b))
best_loss = new_loss
else:
m -= m_adjust
b -= b_adjust
print("y = {0}x + {1}".format(m, b))
Verá el progreso del algoritmo, pero finalmente debería obtener una función ajustada de
aproximadamente y = 1.9395722046562853x + 4.731834051245578, más o menos. Validemos
esta respuesta. Cuando usé Excel o Desmos para realizar una regresión lineal, Desmos me dio y =
1.93939x + 4.73333. ¡Nada mal! ¡Me acerqué bastante!
¿Por qué necesitábamos un millón de iteraciones? A través de la experimentación, encontré esto
con suficientes iteraciones donde la solución ya no mejoraba mucho y convergía de cerca a los
valores óptimos para m y b para minimizar la suma de cuadrados. Encontrará que muchas
bibliotecas y algoritmos de aprendizaje automático tienen un parámetro para la cantidad de
iteraciones a realizar, y hace exactamente eso. Debe tener suficiente para que converja en la
respuesta correcta aproximadamente, pero no tanto como para que pierda tiempo de cálculo
cuando ya ha encontrado una solución aceptable.
Otra pregunta que puede tener es por qué comencé best_loss en un número extremadamente alto.
Hice esto para inicializar la mejor pérdida con un valor que sé que se sobrescribirá una vez que
comience la búsqueda, y luego se comparará con la nueva pérdida de cada iteración para ver si
resulta en una mejora. También podría haber usado un flotador infinito positivo ('inf') en lugar de un
número muy grande.
207
return p
for i in range(1_000_000):
# Select b0 or b1 randomly, and adjust it randomly
random_b = random.choice(range(2))
random_adjust = np.random.normal()
if random_b == 0:
b0 += random_adjust
elif random_b == 1:
b1 += random_adjust
# Calculate total likelihood
true_estimates = sum(math.log(predict_probability(p.x)) \
for p in points if p.y == 1.0)
false_estimates = sum(math.log(1.0 - predict_probability(p.x)) \
for p in points if p.y == 0.0)
total_likelihood = true_estimates + false_estimates
# If likelihood improves, keep the random adjustment. Otherwise revert.
if best_likelihood < total_likelihood:
best_likelihood = total_likelihood
elif random_b == 0:
b0 -= random_adjust
elif random_b == 1:
b1 -= random_adjust
print("1.0 / (1 + exp(-({0} + {1}*x))".format(b0, b1))
print("BEST LIKELIHOOD: {0}".format(math.exp(best_likelihood)))
Consulte el Capítulo 6 para obtener más detalles sobre la estimación de máxima verosimilitud, la
función logística y la razón por la que usamos la función log().
208
Podemos expresar eso como una desigualdad donde x es el número de unidades iPac e y es el
número de unidades iPac Ultra. Ambos deben ser positivos y la Figura A-5 muestra que podemos
graficar en consecuencia.
x + 3y ≤ 20(x ≥ 0, y ≥ 0)
Observe en la Figura A-6 que ahora tenemos una superposición entre estas dos restricciones.
Nuestra solución está en algún lugar de esa superposición y la llamaremos región factible.
Finalmente, estamos maximizando nuestra ganancia Z, que se expresa a continuación, dadas las
cantidades de ganancias para el iPac y el iPac Ultra, respectivamente.
Z = 200x + 300y
209
Si expresamos esta función como una línea, podemos aumentar Z tanto como sea posible hasta
que la línea ya no esté en la región factible. Luego anotamos los valores x e y como se visualizan
en la Figura A-7.
GRÁFICO DE DESMOS DE LA FUNCIÓN OBJETIVO
Si necesita ver esto visualizado de una manera más interactiva y animada, consulte este gráfico en
Desmos.
Figura A-7. Aumentar nuestra línea objetivo hasta que ya no esté en la región factible
Cuando esa línea "apenas toca" la región factible a medida que aumenta la ganancia Z tanto como
sea posible, aterrizará en un vértice, o esquina, de la región factible. Ese vértice proporciona los
valores de x e y que maximizarán la ganancia, como se muestra en la Figura A-8.
Si bien podríamos usar NumPy y un montón de operaciones matriciales para resolver esto
numéricamente, será más fácil usar PuLP como se muestra en el Ejemplo A-11. Tenga en cuenta
que LpVariable define las variables para resolver. LpProblem es el sistema de programación lineal
que agrega restricciones y funciones objetivo utilizando operadores de Python. Luego, las variables
se resuelven llamando a solve() en LpProblem.
Ejemplo A-11. Usando Python PuLP para resolver un sistema de programación lineal
# GRAPH" https://www.desmos.com/calculator/iildqi2vt7
from pulp import *
# declare your variables
x = LpVariable("x", 0) # 0<=x
210
y = LpVariable("y", 0) # 0<=y
# defines the problem
prob = LpProblem("factory_problem", LpMaximize)
# defines the constraints
prob += x + 3*y <= 20
prob += 6*x +2*y <= 45
# defines the objective function to maximize
prob += 200*x + 300*y
# solve the problem
status = prob.solve()
print(LpStatus[status])
# print the results x = 5.9375, y = 4.6875
print(value(x))
print(value(y))
Quizás se pregunte si tiene sentido construir 5.9375 y 4.6875 unidades. Los sistemas de
programación lineal son mucho más eficientes si puede tolerar valores continuos en sus variables,
y quizás pueda simplemente redondearlos después. Pero ciertos tipos de problemas requieren
absolutamente que los números enteros y las variables binarias se manejen discretamente.
Para forzar que las variables x e y se traten como números enteros, proporcione un argumento de
categoría cat=LpInteger como se muestra en el Ejemplo A-12.
Ejemplo A-12. Obligar a que las variables se resuelvan como números enteros
# declare your variables
x = LpVariable("x", 0, cat=LpInteger) # 0<=x
y = LpVariable("y", 0, cat=LpInteger) # 0<=y
Gráficamente, esto significa que llenamos nuestra región factible con puntos discretos en lugar de
una región continua. Nuestra solución no aterrizará necesariamente en un vértice, sino en el punto
más cercano al vértice, como se muestra en la Figura A-9.
Hay un par de casos especiales en la programación lineal, como se muestra en la Figura A-10. A
veces puede haber muchas soluciones. A veces, puede que no haya ninguna solución en absoluto.
Este es solo un ejemplo de introducción rápida a la programación lineal y, lamentablemente, no hay
suficiente espacio en este libro para hacer justicia al tema. Se puede usar para problemas
sorprendentes, incluida la programación de recursos limitados (como trabajadores, trabajos de
servidor o salas), la resolución de Sudokus y la optimización de carteras financieras.
211
Figura A-9. Un sistema de programación lineal discreta
Si quieres saber más, hay algunos buenos videos de YouTube, incluidos PatrickJMT y Josh
Emmanuel. Si desea profundizar en la optimización discreta, el profesor Pascal Van Hentenryck ha
hecho un tremendo servicio organizando un curso en Coursera.
212
# load data
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
df = pd.read_csv('https://bit.ly/3ilJc2C', compression='zip', delimiter=",")
# Extract input variables (all rows, all columns but last column)
# Note we should do some linear scaling here
X = (df.values[:, :-1] / 255.0)
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# Get a count of each group to ensure samples are equitably balanced
print(df.groupby(["class"]).agg({"class" : [np.size]}))
# Separate training and testing data
# Note that I use the 'stratify' parameter to ensure
# each class is proportionally represented in both sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y,
test_size=.33, random_state=10, stratify=Y)
nn = MLPClassifier(solver='sgd',
hidden_layer_sizes=(100, ),
activation='logistic',
max_iter=480,
learning_rate_init=.1)
nn.fit(X_train, Y_train)
print("Training set score: %f" % nn.score(X_train, Y_train))
print("Test set score: %f" % nn.score(X_test, Y_test))
# Display heat map
import matplotlib.pyplot as plt
fig, axes = plt.subplots(4, 4)
# use global min / max to ensure all weights are shown on the same scale
vmin, vmax = nn.coefs_[0].min(), nn.coefs_[0].max()
for coef, ax in zip(nn.coefs_[0].T, axes.ravel()):
ax.matshow(coef.reshape(28, 28), cmap=plt.cm.gray, vmin=.5 * vmin, vmax=.5 * vmax)
ax.set_xticks(())
ax.set_yticks(())
plt.show()
Capítulo 1
1. 62,6738 es racional porque tiene un número finito de decimales y, por lo tanto, se puede
expresar como una fracción 626738 / 10000.
2. 10710-5 = 107+-5 = 102 = 100
3.
4.
5. El monto resultante sería de $1,161.47. El script de Python es el siguiente:
from math import exp
p = 1000
r = .05
t=3
n = 12
a = p * (1 + (r/n))**(n * t)
print(a) # prints 1161.4722313334678
6. El monto resultante sería $1161.83. El script de Python es el siguiente:
213
from math import exp
p = 1000 # principal, starting amount
r = .05 # interest rate, by year
t = 3.0 # time, number of years
a = p * exp(r*t)
print(a) # prints 1161.834242728283
7. La derivada calcula 6 x, lo que haría que la pendiente en x = 3 fuera 18. El código SymPy
es el siguiente:
from sympy import *
# Declare 'x' to SymPy
x = symbols('x')
# Now just use Python syntax to declare function
f = 3*x**2 + 1
# Calculate the derivative of the function
dx_f = diff(f)
print(dx_f) # prints 6*x
print(dx_f.subs(x,3)) # 18
8. El área bajo la curva entre 0 y 2 es 10. El código SymPy es el siguiente:
from sympy import *
# Declare 'x' to SymPy
x = symbols('x')
# Now just use Python syntax to declare function
f = 3*x**2 + 1
# Calculate the integral of the function with respect to x
# for the area between x = 0 and 2
area = integrate(f, (x, 0, 2))
print(area) # prints 10
Capitulo 2
1. 0,3 × 0,4 = 0,12; consulte "Probabilidades conjuntas".
2. (1 – 0,3) + 0,4 – (0,03 × 0,4) = 0,98; consulte las "Probabilidades de unión" y recuerde
que estamos buscando SIN LLUVIA, así que reste esa probabilidad de 1.0.
3. 0,3 × 0,2 = 0,06; consulte “Probabilidad condicional y teorema de Bayes”.
4. El siguiente código de Python calcula una respuesta de 0,822, sumando las
probabilidades de que 50 o más pasajeros no se presenten:
from scipy.stats import binom
n = 137
p = .40
p_50_or_more_noshows = 0.0
for x in range(50,138):
p_50_or_more_noshows += binom.pmf(x, n, p)
print(p_50_or_more_noshows) # 0.822095588147425
5. Usando la distribución beta que se muestra en el siguiente código SciPy, obtenga el área
hasta 0.5 y réstelo de 1.0. El resultado es de aproximadamente 0,98, por lo que es muy
poco probable que esta moneda sea justa.
from scipy.stats import beta
heads = 8
tails = 2
p = 1.0 - beta.cdf(.5, heads, tails)
print(p) # 0.98046875
Capítulo 3
1. La media es 1,752 y la desviación estándar es aproximadamente 0,02135. El código de
Python es el siguiente:
214
from math import sqrt
sample = [1.78, 1.75, 1.72, 1.74, 1.77]
def mean(values):
return sum(values) /len(values)
def variance_sample(values):
mean = sum(values) / len(values)
var = sum((v - mean) ** 2 for v in values) / len(values)
return var
def std_dev_sample(values):
return sqrt(variance_sample(values))
mean = mean(sample)
std_dev = std_dev_sample(sample)
print("MEAN: ", mean) # 1.752
print("STD DEV: ", std_dev) # 0.02135415650406264
2. Use the CDF to get the value between 30 and 20 months, which
is an area of about 0.06. The Python code is as follows:
from scipy.stats import norm
mean = 42
std_dev = 8
x = norm.cdf(30, mean, std_dev) - norm.cdf(20, mean, std_dev)
print(x) # 0.06382743803380352
3. Hay un 99 % de probabilidad de que el diámetro medio del filamento de un rollo esté entre
1,7026 y 1,7285. El código de Python es el siguiente:
from math import sqrt
from scipy.stats import norm
def critical_z_value(p, mean=0.0, std=1.0):
norm_dist = norm(loc=mean, scale=std)
left_area = (1.0 - p) / 2.0
right_area = 1.0 - ((1.0 - p) / 2.0)
return norm_dist.ppf(left_area), norm_dist.ppf(right_area)
def ci_large_sample(p, sample_mean, sample_std, n):
# Sample size must be greater than 30
lower, upper = critical_z_value(p)
lower_ci = lower * (sample_std / sqrt(n))
upper_ci = upper * (sample_std / sqrt(n))
return sample_mean + lower_ci, sample_mean + upper_ci
print(ci_large_sample(p=.99, sample_mean=1.715588,
sample_std=0.029252, n=34))
# (1.7026658973748656, 1.7285101026251342)
215
if p_value <= .05:
print("Passes two-tailed test")
else:
print("Fails two-tailed test")
# Two-tailed P-value 0.01888333596496139
# Passes two-tailed test
Capítulo 4
1. El vector aterriza en [2, 3]. El código de Python es el siguiente:
from numpy import array
v = array([1,2])
i_hat = array([2, 0])
j_hat = array([0, 1.5])
# fix this line
basis = array([i_hat, j_hat])
# transform vector v into w
w = basis.dot(v)
print(w) # [2. 3.]
4. Sí, porque la multiplicación de matrices nos permite combinar varias matrices en una sola
matriz que representa una transformación consolidada.
5. x = 19,8, y = –5,4, z = –6. El código es el siguiente:
from numpy import array
from numpy.linalg import inv
A = array([
[3, 1, 0],
[2, 4, 1],
[3, 1, 8]
])
B = array([
54,
12,
6
])
X = inv(A).dot(B)
print(X) # [19.8 -5.4 -6. ]
216
6. Sí, es linealmente dependiente. Aunque tenemos cierta imprecisión de punto flotante con
NumPy, el determinante es efectivamente 0:
from numpy.linalg import det
from numpy import array
i_hat = array([2, 6])
j_hat = array([1, 3])
basis = array([i_hat, j_hat]).transpose()
print(basis)
determinant = det(basis)
print(determinant) # -3.330669073875464e-16
Capítulo 5
1. Hay muchas herramientas y enfoques para realizar una regresión lineal como aprendimos
en el Capítulo 5, pero aquí está la solución usando scikit-learn. La pendiente es 1.75919315 y
la intersección es 4.69359655.
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# Import points
df = pd.read_csv('https://bit.ly/3C8JzrM', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# Fit a line to the points
fit = LinearRegression().fit(X, Y)
# m = 1.75919315, b = 4.69359655
m = fit.coef_.flatten()
b = fit.intercept_.flatten()
print("m = {0}".format(m))
print("b = {0}".format(b))
# show in chart
plt.plot(X, Y, 'o') # scatterplot
plt.plot(X, m*X+b) # line
plt.show()
2. Obtenemos una alta correlación de 0.92421 y un valor de prueba de 23.8355 con un rango
estadísticamente significativo de ±1.9844. Esta correlación es definitivamente útil y
estadísticamente significativa. El código es el siguiente:
import pandas as pd
# Read data into Pandas dataframe
df = pd.read_csv('https://bit.ly/3C8JzrM', delimiter=",")
# Print correlations between variables
correlations = df.corr(method='pearson')
print(correlations)
# OUTPUT:
#xy
217
# x 1.00000 0.92421
# y 0.92421 1.00000
# Test for statistical significance
from scipy.stats import t
from math import sqrt
# sample size
n = df.shape[0]
print(n)
lower_cv = t(n - 1).ppf(.025)
upper_cv = t(n - 1).ppf(.975)
# retrieve correlation coefficient
r = correlations["y"]["x"]
# Perform the test
test_value = r / sqrt((1 - r ** 2) / (n - 2))
print("TEST VALUE: {}".format(test_value))
print("CRITICAL RANGE: {}, {}".format(lower_cv, upper_cv))
if test_value < lower_cv or test_value > upper_cv:
print("CORRELATION PROVEN, REJECT H0")
else:
print("CORRELATION NOT PROVEN, FAILED TO REJECT H0 ")
# Calculate p-value
if test_value > 0:
p_value = 1.0 - t(n - 1).cdf(test_value)
else:
p_value = t(n - 1).cdf(test_value)
# Two-tailed, so multiply by 2
p_value = p_value * 2
print("P-VALUE: {}".format(p_value))
"""
TEST VALUE: 23.835515323677328
CRITICAL RANGE: -1.9844674544266925, 1.984467454426692
CORRELATION PROVEN, REJECT H0
P-VALUE: 0.0 (extremely small)
"""
3. En x = 50 , el intervalo de predicción está entre 50,79 y 134,51. El código es el siguiente:
import pandas as pd
from scipy.stats import t
from math import sqrt
# Load the data
points = list(pd.read_csv('https://bit.ly/3C8JzrM', delimiter=",")
\
.itertuples())
n = len(points)
# Linear Regression Line
m = 1.75919315
b = 4.69359655
# Calculate Prediction Interval for x = 50
x_0 = 50
x_mean = sum(p.x for p in points) / len(points)
t_value = t(n - 2).ppf(.975)
standard_error = sqrt(sum((p.y - (m * p.x + b)) ** 2 for p in
points) / \
(n - 2))
margin_of_error = t_value * standard_error * \
sqrt(1 + (1 / n) + (n * (x_0 - x_mean) ** 2) / \
(n * sum(p.x ** 2 for p in points) - \
218
sum(p.x for p in points) ** 2))
predicted_y = m*x_0 + b
# Calculate prediction interval
print(predicted_y - margin_of_error, predicted_y +
margin_of_error)
# 50.792086501055955 134.51442159894404
4. Los conjuntos de datos de prueba funcionan moderadamente bien cuando se dividen en
tercios y se evalúan con k-fold, donde k = 3. Obtendrá una media de aproximadamente 0,83
en MSE y una desviación estándar de 0,03 en los tres conjuntos de datos.
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold, cross_val_score
df = pd.read_csv('https://bit.ly/3C8JzrM', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)\
Y = df.values[:, -1]
# Perform a simple linear regression
kfold = KFold(n_splits=3, random_state=7, shuffle=True)
model = LinearRegression()
results = cross_val_score(model, X, Y, cv=kfold)
print(results)
print("MSE: mean=%.3f (stdev-%.3f)" % (results.mean(),
results.std()))
"""
[0.86119665 0.78237719 0.85733887]
MSE: mean=0.834 (stdev-0.036)
"""
Capítulo 6
1. La precisión es extremadamente alta cuando ejecuta esto a través de scikit-learn. Cuando
lo ejecuto, obtengo al menos un 99,9% de precisión en promedio con los pliegues de prueba.
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import KFold, cross_val_score
# Load the data
df = pd.read_csv("https://bit.ly/3imidqa", delimiter=",")
X = df.values[:, :-1]
Y = df.values[:, -1]
kfold = KFold(n_splits=3, shuffle=True)
model = LogisticRegression(penalty='none')
results = cross_val_score(model, X, Y, cv=kfold)
print("Accuracy Mean: %.3f (stdev=%.3f)" % (results.mean(),
results.std()))
219
# Extract output column (all rows, last column)\
Y = df.values[:, -1]
model = LogisticRegression(solver='liblinear')
X_train, X_test, Y_train, Y_test = train_test_split(X, Y,
test_size=.33)
model.fit(X_train, Y_train)
prediction = model.predict(X_test)
"""
The confusion matrix evaluates accuracy within each category.
[[truepositives falsenegatives]
[falsepositives truenegatives]]
The diagonal represents correct predictions,
so we want those to be higher
"""
matrix = confusion_matrix(y_true=Y_test, y_pred=prediction)
print(matrix)
3. A continuación se muestra un caparazón interactivo para probar los colores ingresados por
el usuario. Considere probar negro (0,0,0) y blanco (255,255,255) para ver si las fuentes
oscuras y claras, respectivamente, se predicen correctamente.
import pandas as pd
from sklearn.linear_model import LogisticRegression
import numpy as np
from sklearn.model_selection import train_test_split
# Load the data
df = pd.read_csv("https://bit.ly/3imidqa", delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)
Y = df.values[:, -1]
model = LogisticRegression(solver='liblinear')
X_train, X_test, Y_train, Y_test = train_test_split(X, Y,
test_size=.33)
model.fit(X_train, Y_train)
prediction = model.predict(X_test)
# Test a prediction
while True:
n = input("Input a color {red},{green},{blue}: ")
(r, g, b) = n.split(",")
x = model.predict(np.array([[int(r), int(g), int(b)]]))
if model.predict(np.array([[int(r), int(g), int(b)]]))[0] ==0.0:
print("LIGHT")
else:
print("DARK")
5. Sí, la regresión logística es muy efectiva para predecir fuentes claras u oscuras para un color
de fondo determinado. No solo la precisión es extremadamente alta, sino que la matriz de
confusión tiene números altos en la diagonal superior derecha a la inferior izquierda con
números más bajos en las otras celdas.
Capítulo 7
Obviamente, hay mucha experimentación y alquimia que puede probar con diferentes capas
ocultas, funciones de activación, diferentes tamaños de conjuntos de datos de prueba, etc. Traté de
usar una capa oculta con tres nodos con una activación ReLU y luché para obtener buenas
predicciones en mi conjunto de datos de prueba. Las matrices de confusión y la precisión fueron
consistentemente pobres y cualquier cambio de configuración que ejecuté funcionó igual de mal.
Las razones por las que la red neuronal probablemente está fallando son 1) el conjunto de datos de
prueba es demasiado pequeño para una red neuronal (que consume muchos datos) y 2) existen
modelos más simples y efectivos como la regresión logística para este tipo de problema. Eso no
quiere decir que no pueda encontrar una configuración que funcione, pero debe tener cuidado de
220
no piratear su camino hacia un buen resultado que se ajuste demasiado a los pocos datos de
entrenamiento y prueba que tiene.
Aquí está el código scikit-learn que utilicé:
import pandas as pd
# load data
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
df = pd.read_csv('https://tinyurl.com/y6r7qjrp', delimiter=",")
# Extract input variables (all rows, all columns but last column)
X = df.values[:, :-1]
# Extract output column (all rows, last column)
Y = df.values[:, -1]
# Separate training and testing data
X_train, X_test, Y_train, Y_test = train_test_split(X, Y,
test_size=1/3)
nn = MLPClassifier(solver='sgd',
hidden_layer_sizes=(3, ),
activation='relu',
max_iter=100_000,
learning_rate_init=.05)
nn.fit(X_train, Y_train)
print("Training set score: %f" % nn.score(X_train, Y_train))
print("Test set score: %f" % nn.score(X_test, Y_test))
print("Confusion matrix:")
matrix = confusion_matrix(y_true=Y_test, y_pred=nn.predict(X_test))
print(matrix)
Sobre el Autor
Thomas Nield es el fundador de Nield Consulting Group, así como instructor en O'Reilly
Media y la Universidad del Sur de California. Le gusta hacer que el contenido técnico sea
identificable y relevante para aquellos que no están familiarizados con él o se sienten
intimidados por él. Thomas imparte regularmente clases sobre análisis de datos,
aprendizaje automático, optimización matemática, seguridad del sistema de IA e
inteligencia artificial práctica. Es autor de dos libros, Getting Started with SQL (O'Reilly) y
Learning RxJava (Packt). También es el fundador e inventor de Yawman Flight, una
empresa que desarrolla controles portátiles universales para simulación de vuelo y
vehículos aéreos no tripulados.
Colofón
Los animales de la portada de Essential Math for Data Science son ratones de hierba de
cuatro rayas ( Rhabdomys pumilio ). Estos roedores se encuentran en la mitad sur del
continente africano, en hábitats variados como sabanas, desiertos, tierras de cultivo,
matorrales e incluso ciudades. Como sugiere su nombre común, este animal tiene un
conjunto distintivo de cuatro rayas oscuras que recorren su espalda. Incluso al nacer, estas
rayas son visibles como líneas pigmentadas en la piel sin pelo del cachorro.
La coloración del pelaje del ratón de pasto varía de marrón oscuro a blanco grisáceo, con
lados y vientres más claros. En general, la especie crece entre 18 y 21 centímetros de
largo (sin contar la cola, que es aproximadamente igual a la longitud del cuerpo) y pesa
entre 30 y 55 gramos. El ratón es más activo durante el día y tiene una dieta omnívora de
semillas, plantas e insectos. En los meses de verano, tiende a comer más material vegetal
y de semillas, y mantiene las reservas de grasa para afrontar épocas de escasez de
alimentos.
221
Los ratones de pasto de cuatro rayas son fáciles de observar dada su amplia gama, y se
ha observado que cambian entre estilos de vida solitarios y sociales. Durante la temporada
de reproducción, tienden a permanecer separados (quizás para evitar una competencia
reproductiva excesiva) y las hembras son territoriales en sus madrigueras. Sin embargo,
fuera de eso, los ratones se congregan en grupos para buscar comida, evitar a los
depredadores y acurrucarse para calentarse.
Muchos de los animales de las portadas de O'Reilly están en peligro de extinción; todos
ellos son importantes para el mundo.
La ilustración de la portada es de Karen Montgomery, basada en un grabado antiguo del
Museo de Historia Natural. Las fuentes de la portada son Gilroy Semibold y Guardian
Sans. La fuente del texto es Adobe Minion Pro; la fuente del encabezado es Adobe Myriad
Condensed; y la fuente del código es Ubuntu Mono de Dalton Maag.
222