Tema 1 - Lenguajes de Programacion.pdf
Document Details
Full Transcript
Programación de Inteligencia Artificial Conforme a contenidos del «Curso de Especialización en Inteligencia Artificial y Big Data» Universidad de Castilla-La Mancha Escuela Superior de Informática Ciudad Real ...
Programación de Inteligencia Artificial Conforme a contenidos del «Curso de Especialización en Inteligencia Artificial y Big Data» Universidad de Castilla-La Mancha Escuela Superior de Informática Ciudad Real Índice general 1. Lenguajes de programación 1 1.1. Introducción a la programación................... 1 1.1.1. El proceso de desarrollo de software............ 2 1.1.2. Aspectos fundamentales de desarrollo........... 8 1.1.3. Lenguajes de programación compilados e interpretados.. 12 1.1.4. Resumen.......................... 16 1.2. Programación de Inteligencia Artificial............... 16 1.2.1. Consideraciones previas.................. 17 1.2.2. Características deseables en un lenguaje de programación para IA........................... 18 1.2.3. Caso práctico. Búsqueda de patrones en archivos de texto. 28 1.2.4. Resumen.......................... 31 1.3. Lenguajes de programación de IA................. 32 1.3.1. Visión general....................... 32 1.3.2. Python como lenguaje de referencia para IA........ 33 1.3.3. R: más allá de la estadística................ 38 1.3.4. Caso práctico en Python: Piedra, Papel o Tijeras!..... 47 1.3.5. Resumen.......................... 59 2. Plataformas de Inteligencia Artificial 61 2.1. Introducción a las Plataformas de IA................ 62 2.1.1. Computación en la nube para la IA............. 62 [II] ÍNDICE GENERAL 2.1.2. Beneficios de la computación en la nube.......... 64 2.1.3. El concepto de plataforma de IA.............. 67 2.1.4. Desarrollo de aplicaciones de IA en entornos cloud.... 67 2.1.5. Oportunidades de las plataformas de IA.......... 73 2.1.6. Resumen.......................... 74 2.2. Detalles de las Plataformas de IA.................. 75 2.2.1. Visión general....................... 75 2.2.2. Fundamentos básicos.................... 76 2.2.3. Aspectos fundamentales de las plataformas de IA..... 78 2.2.4. Comparativa........................ 87 2.2.5. Elección de la plataforma.................. 88 2.2.6. Resumen.......................... 91 Índice general 5. Convergencia Tecnológica 1 5.1. Introducción a la convergencia tecnológica............. 1 5.2. Visión general............................ 2 5.3. Ventajas de la convergencia tecnológica.............. 6 5.4. Plataformas para la conexión tecnológica............. 7 5.4.1. Power Platform....................... 9 5.5. Análisis de Power Automate.................... 11 5.5.1. Ejemplo 1. Hola Mundo y uso de plantillas........ 11 5.5.2. Ejemplo 2. Creación de flujo automatizado (Creación de Archivo a Excel)...................... 13 5.5.3. Ejemplo 3. Creación de flujo programado y utilización de variables de flujo...................... 15 5.5.4. Ejemplo 4. Creación de flujo de escritorio......... 16 5.5.5. Ejemplo de llamado mediante API REST......... 17 [II] ÍNDICE GENERAL 5.7. Sistemas Blockchain........................ 456 5.7.1. Dificultades de los sistemas descentralizados....... 456 5.7.2. El concepto de bloque................... 458 5.7.3. Qué es el Proof of Work y ajustes............. 459 5.7.4. Seguridad en Blockchain.................. 461 5.7.5. ¡Blockchain es una estructura de datos descentralizada!.. 462 5.8. Introducción a Ethereum...................... 463 5.8.1. Ethereum: Un blockchain de propósito general...... 465 5.8.2. Aplicaciones Descentralizadas (DApps).......... 467 5.8.3. Desarrollando en Solidity................. 467 5.8.4. Siguientes pasos...................... 480 Listado de acrónimos API Application Programming Interface AWS Amazon Web Services BASIC Beginner’s All-purpose Symbolic Instruction Code CLI Command-Line Interface CPD Centro de Procesamiento de Datos CPU Computer Processing Unit CPU Central Processing Unit D EVO PS Development and Operations FANN Fast Artificial Neural Network FSF Free Software Foundation GCC GNU Compiler Collection GPL GNU General Public License GPU Graphics Processing Unit GUI Graphical User Interface HTTP Hypertext Transfer Protocol HTTPS Hypertext Transfer Protocol Secure IA Inteligencia Artificial JSON JavaScript Object Notation KISS Keep it Simple Stupid! LISP List Processing MIT Massachusetts Institute of Technology MLO PS Machine Learning Operations NIST National Institute of Standards and Technology NLTK Natural Language Toolkit O PEN CV Open Source Computer Vision Library PEP Python Enhancement Proposal POO Programación Orientada a Objetos P ROLOG Logic Programming ACRÓNIMOS PSF Python Software Foundation RAM Random Access Memory REST Representational State Transfer SRP Single-Responsibility Principle SDK Software Development Kit TDD Test-Driven Development TI Tecnologías de la Información TRL Technology Readiness Levels URL Uniform Resource Locator Lenguajes de programación Capítulo 1 David Vallejo Fernández, Santiago Sánchez Sobrino E ste capítulo sirve como punto introductorio a la programación, abordándola desde la perspectiva general del proceso de desarrollo de software y au- mentando así el alcance que el primer término realmente tiene. El contexto para el cual se irá particularizando, no obstante, es el desarrollo de aplicaciones de IA (Inteligencia Artificial). Adicionalmente, se realiza un recorrido a alto nivel de aspectos fundamentales y buenas prácticas de desarrollo. En dicho recorrido, se discuten los conceptos generales de los lenguajes de programación compilados e interpretados, ofreciendo al lector una comparativa y estableciendo directrices que faciliten la toma de decisiones, a nivel de tecnología, cuando se aborda un proyecto de IA. En este sentido, se discute un conjunto básico de las principales características deseables de un lenguaje de programación para aplicaciones de IA, con un especial énfasis en las que resultan relevantes para el programador. Posteriormente, y desde un punto de vista más concreto, se aborda la introducción de dos lenguajes de programación de referencia, Python y R, con respectivos casos prácticos que ejemplifican sus bondades. Finalmente, el capítulo termina detallando aspectos más específicos que gene- ralmente son transversales en el desarrollo de aplicaciones de IA, como el proce- samiento de datos y el uso de lenguajes de marcas. 1.1. Introducción a la programación Esta sección ofrece una una visión general del proceso de desarrollo de soft- ware, contextualizándolo para destacar la importancia que tiene más allá de la fase puramente vinculada a la programación. En este recorrido general y sintetizado, se abordará la relevancia de la capacidad de manejar abstracciones y de plantear 1 Capítulo 1 :: Lenguajes de programación soluciones simples. Asimismo, esta visión se complementará con un listado aco- tado de buenas prácticas de desarrollo y generación de clean code. Por otro lado, esta sección resume las principales características de los lenguajes de programa- ción compilados e interpretados, en el marco de herramientas para el desarrollo de proyectos de IA. 1.1.1. El proceso de desarrollo de software La construcción de software es un proceso complejo y sofisticado que va más allá de lo que comúnmente conocemos como programación. En este apartado se ofrece una visión general de las fases más relevantes en las que este proceso se estructura. Asimismo, también se introducen dos aspectos que están relacionados con destrezas fundamentales a la hora de abordarlo: i) la capacidad de abstracción mediante el uso de metáforas y ii) la importancia de la simplicidad a la hora de plantear cualquier tipo de solución. Fases principales La industria del desarrollo o construcción de software ha madurado significa- tivamente en los últimos 30 años, siendo posible identificar un conjunto de fases que conforman dicho desarrollo. Así, y desde una perspectiva de la ingeniería del software, las principales fases son las siguientes [McC04]: Definición del problema. En esta fase se especifica el prerrequisito más re- levante de todo proyecto software, el cual consiste en una identificación clara y precisa del problema que se pretende resolver. En este punto, no se hace referencia a posibles soluciones. La salida esperada es un breve documento, de una o dos páginas, que defina el problema. Especificación de requisitos. Esta fase consiste en generar una descripción detallada acerca de lo que el sistema software debe hacer. Uno de los aspec- tos fundamentales de esta fase reside en garantizar que la funcionalidad del sistema esté guiada por el usuario final, y no por el programador. En otras palabras, un listado exhaustivo de requisitos representa el contrato entre el cliente y el desarrollador y permite que el cliente lo revise y valide antes de abarcar otras fases. Es importante prestar mucha atención a esta fase con el objetivo de minimizar el número de cambios sobre el sistema, especialmente en las etapas de diseño y desarrollo. Planificación del desarrollo. Esta fase tiene como objetivo detallar la pla- nificación del resto de fases de la construcción de software. Existe una de- pendencia clara entre esta fase y la anterior, ya que el alcance del proyecto vendrá determinado por el número y complejidad de los requisitos previa- mente identificados. 1.1. Introducción a la programación Arquitectura software o diseño de alto-nivel. Esta fase trata el diseño glo- bal de un proyecto software, entendido como la actividad que sirve de nexo entre la especificación de requisitos y la codificación. En proyectos comple- jos, la arquitectura software define el mapa global sobre el que se apoyará un conjunto de aspectos de diseño más específicos que, posteriormente, se pue- dan utilizar como punto de referencia a la hora de programar. En esta fase se manejan conceptos como módulos, subsistemas o paquetes. Diseño detallado. Esta fase persigue la especificación de un diseño más cer- cano al código, considerando las entidades que forman parte del mismo y sus relaciones. En esta fase se manejan conceptos como el tradicional diagrama de clases, en caso de utilizar un paradigma de programación orientada a obje- tos. Tanto en esta fase, como en la anterior, debe prestarse atención a caracte- rísticas como la reducción de la complejidad, la facilidad de mantenimiento, el bajo acoplamiento, la extensibilidad o la capacidad de reutilización, entre otras. Codificación y depuración. Esta fase engloba tanto el proceso de progra- mación en sí, entendido como la escritura de un conjunto de sentencias que persiguen la generación de un resultado, como el proceso de depuración o identificación y corrección de las causas que originan errores. En función del paradigma de programación empleado, el nivel de abstracción emplea- do será diferente. Por ejemplo, en la programación estructurada, el concepto de rutina o función representa un elemento fundamental, mientras que en la programación orientada a objetos la clase es la herramienta esencial para programar. Pruebas unitarias. Esta fase aborda la escritura de código que posibilite la prueba de cierto código, como por ejemplo una clase, de manera indepen- diente al resto de código que conforma el sistema. La fase de pruebas tiene una gran relevancia para reducir el número de errores que se puedan producir en tiempo de ejecución. De hecho, existen procesos de desarrollo de software que giran alrededor del concepto de prueba, como por ejemplo TDD (Test- Driven Development) [Bec04]. Pruebas de integración. Esta fase tiene como objetivo combinar la ejecu- ción y pruebas de dos o más clases, módulos o subsistemas, de forma que se puedan evaluar las interacciones existentes entre ellas. Es importante reali- zar pruebas de integración desde el principio hasta el final del desarrollo del proyecto. Integración. Esta fase aborda la problemática de mezclar nuevo código con código existente. Al igual que ocurre con la fase de pruebas de integración, resulta recomendable abordar esta fase lo antes posible para mitigar la com- plejidad de la misma cuando el proyecto crece de tamaño. La idea de inte- gración continua se ha acuñado en los últimos años como filosofía de traba- jo [HF10]. Capítulo 1 :: Lenguajes de programación Pruebas e Especificación Diseño Desarrollo Mantenimiento integración Pruebas unitarias Definición Pruebas de del problema Arquitectura Mantenimiento Codificación integración Especificación software Actualizaciones Depuración Integración de requisitos Diseño Refactorización Pruebas de Planificación detallado sistemas del desarrollo Figura 1.1: Fases globales del proceso de desarrollo de software. Pruebas de sistemas. Esta fase representa la última actividad relativa a prue- bas de software antes de llevar a cabo el despliegue de un sistema, o una actualización del mismo, en producción. Mantenimiento. Esta última fase está relacionada con la actualización del código, e incluso en ocasiones del diseño, lo cual deriva en procesos de re- factorización, que conforman el núcleo de un proyecto de desarrollo de soft- ware. Las actualizaciones de código son una consecuencia de los cambios introducidos en un proyecto después de su lanzamiento. Aunque pueda re- sultar sorprendente, los costes de mantenimiento suelen representar la mayor partida económica de un proyecto software a largo plazo. Más allá del mantenimiento. De acuerdo a Fred Books, “el problema funda- mental del mantenimiento de los programas es que arreglar un defecto tiene aa una probabilidad considerable (20-50 %) de introducir otro. Por lo tanto, todo el proceso consiste en dar dos pasos adelante y uno atrás”. Como puede apreciar, en el exhaustivo listado anterior se reflejan las 4 grandes fases generales que probablemente ya tuviera interiorizadas: i) especificación, ii) diseño, iii) desarrollo y iv) pruebas. La figura 1.1 muestra este listado de manera visual. Desafortunadamente, no es sencillo encontrar un equilibrio entre el nivel de formalidad del anterior listado y la posibilidad de relajar u obviar alguna de las fases mencionadas. De hecho, es bastante probable que, internamente, las haya agrupado alrededor del concepto de programación, especialmente si hasta ahora ha trabajado en proyectos personales o prototipos que no vayan a alcanzar un nivel significativo de madurez tecnológica. Si bien la fase de programación o codificación representa una de las principales actividades en proyectos de prototipado rápido, es importante conocer el contexto general del proceso completo de construcción de software. Esto es especialmen- te relevante tanto para saber dónde poner el esfuerzo en cada momento como para no olvidar las dependencias, y consecuencias, existentes entre fases. A modo de ejemplo, un error en la fase de diseño puede acarrear consecuencias desastrosas en la fase de codificación y depuración, como el incremento de la complejidad de la solución o el aumento en el tiempo empleado en programar la misma. 1.1. Introducción a la programación Niveles de Madurez Tecnológica. Procedentes del término en inglés TRL (Technology Readiness Levels), representan una medida de la madurez de una tecnología, facilitando el uso de una nomenclatura consistente y uni- forme. Los niveles TRL utilizados en la Unión Europea van de 1 a 9: 1) prin- cipios básicos observados, 2) concepto de tecnología formulado, 3) prueba experimental de concepto, 4) tecnología validada en laboratorio, 5) tecnología validada en un entorno relevante, 6) tecnología demostrada en un entorno re- levante, 7) demostración del prototipo del sistema en un entorno operativo, 8) sistema completo y calificado, 9) sistema real probado en el entorno operativo. Abstracción y uso de metáforas Una de las capacidades más relevantes que un desarrollador debe adquirir es la capacidad de abstracción, es decir, la capacidad de generalizar y reutilizar ideas independientemente del contexto particular. En este sentido, el uso de metáforas ha resultado ser tremendamente útil conforme la industria del desarrollo de software ha ido evolucionando. De hecho, el concepto de modelado software está inherente- mente relacionado con la aplicación de metáforas o analogías, las cuales facilitan la generación de resultados cuando se compara un tema o dominio que no se entiende del todo bien, al menos al principio, con otro que sí se entiende de manera efectiva. Existe un amplio listado de metáforas que han servido para simplificar la com- plejidad asociada al desarrollo de software desde una perspectiva integral [McC04]. Quizá una de las más representativas viene derivada de la expresión escribir código, la cual sugiere que desarrollar un programa es similar a escribir una carta. Otra me- táfora bastante conocida es la que compara el hecho de cultivar una cosecha con la creación de software. Esta se basa en la idea de plantear soluciones incrementales: diseño de una parte del sistema, codificación de esa parte, pruebas e integración. En esencia, plantear una solución que se va generando poco a poco minimiza el riesgo de encontrarse en una situación donde la complejidad subyacente es tal que dificulta enormemente la gestión de un proyecto. La importancia de documentar código. En relación a la analogía entre es- cribir código y escribir una carta, en este punto se anima al lector a recordar la clásica afirmación documentation is a love letter that you write to your future self. En el contexto del desarrollo de aplicaciones de inteligencia artificial, la uti- lización de metáforas es especialmente relevante. Considere, por ejemplo, que el concepto de red neuronal es en realidad una analogía, aunque inexacta, de cómo funcionan y se comunican las neuronas en un cerebro biológico. Hoy en día, las redes neuronales se emplean en programas capaces de reconocer patrones y apren- der de forma automática, entre otras funciones, y representan el corazón del deep learning. Capítulo 1 :: Lenguajes de programación Otro caso relevante es el de los sistemas basados en reglas, ya sean definidas por un experto humano o aprendidas de manera automática, los cuales recrean la forma en la que los humanos solemos pensar: si se da una determinada situación, definida por un conjunto de condiciones, entonces se lanzan una o varias acciones. Una metáfora más reciente es la de los web crawlers, entendidos como programas que analizan y extraen enlaces o hipervínculos de páginas web, no dejan de ser una analogía de una telaraña explorada por una cazadora (araña) en búsqueda de presas (información). El concepto de web scrapping, relacionado con el anterior, se centra en la extracción de datos o contenido. Incluso existen teorías científicas, como la lógica difusa o fuzzy logic [Zad99], que acercan la forma en la que nosotros razonamos a la definición de modelos ma- temáticos que permitan su uso por parte de máquinas o programas. En esencia, la lógica difusa es una forma de lógica multi-valuada en el que los valores de verdad de las variables pueden tomar valores en el rango [0, 1]. Por el contrario, en la lógi- ca booleana las variables solo pueden tomar los valores enteros de 0 o 1. La lógica difusa se fundamenta en el hecho de que las personas toman decisiones atendiendo a información imprecisa o vaga (típicamente, no numérica). Así, surge la idea de conjunto difuso como medio matemático para representar dicha información. Una de las aplicaciones clásicas de la lógica difusa, combinada con los sistemas basados en reglas, está representada por los sistemas de control. De hecho, una parte sig- nificativa de los productos de consumo, como por ejemplo el controlador del aire acondicionado, se basan en lógica difusa. Otra metáfora significativa es la de agente inteligente, definido en el ámbito de la IA como cualquier entidad capaz de percibir lo que ocurre en el medio o con- texto en el que habita, mediante la ayuda de sensores, y actuar en consecuencia, mediante la ayuda de actuadores, generando normalmente algún tipo de cambio en dicho medio o contexto (ver figura 1.2). En este caso, la metáfora de agente se hace directamente con el concepto de ser humano o individuo. Cuatro son las ca- racterísticas fundamentales de un agente inteligente: i) autonomía, de manera que un agente actúa sin la intervención directa de terceras partes, ii) habilidad social, de forma que los agentes interactúan entre sí y se comunican para alcanzar un ob- jetivo común, iii) reactividad, de manera que un agente actúa en función de las percepciones del entorno, y iv) proactividad, de manera que un agente puede tomar la iniciativa en lugar de ser puramente reactivo. Los agentes inteligentes confor- man sistemas multi-agente, los cuales están asociados al dominio de la inteligencia artificial distribuida e incluso al concepto de programación orientada a agentes. La importancia de la simplicidad Si la capacidad de pensar, modelar y desarrollar de forma abstracta es una de las habilidades más importantes de un ingeniero software, la habilidad de plan- tear soluciones sencillas quizá sea la que más impacto y alcance tiene cuando se aborda cualquier proyecto software. Piense que los problemas que tendrá que resol- ver, desde el punto de vista de la construcción de software, son lo suficientemente 1.1. Introducción a la programación Agente ver acción siguiente estado Entorno Figura 1.2: Visión abstracta del funcionamiento interno de un agente. complejos como para que sus soluciones también lo sean. Recuerde, además, y co- mo se ha mencionado anteriormente, que los costes de mantenimiento se suelen llevar la mayor parte de costes económicos de un proyecto software a largo plazo. Todo guarda relación con la simplicidad. Por otro lado, el desarrollo de soluciones basadas en IA no deja de ser un caso particular de construcción de software, por lo que el planteamiento de soluciones simples sigue teniendo la misma o más importancia. De hecho, el desarrollo de so- luciones basadas en IA, al menos en lo que se refiere a proyectos experimentales, está íntimamente relacionado con el concepto de prototipado, donde el pragmatis- mo y la simplicidad deberían ser protagonistas. Suele ser bastante común prototipar una solución en un lenguaje como Python o LUA para, posteriormente, generar una solución con más alcance en C++ (especialmente si existen restricciones en térmi- nos de eficiencia, como se discutirá más adelante). Tomando decisiones. Ante un problema para el cual resulta posible aplicar diferentes soluciones, ¿cuánto tiempo dedica a evaluar qué solución es la más sencilla y, por lo tanto, ofrece mejores perspectivas de desarrolo y mantenibi- lidad de código? Puede que el lector esté familiarizado con el principio KISS (Keep it Simple Stupid!), el cual pone de manifiesto que la mayoría de sistemas funcionan mejor si se mantienen lo más sencillos posible. En otras palabras, la simplicidad se convierte en un objetivo principal en el diseño, obviando cualquier atisbo de complejidad innecesaria. Capítulo 1 :: Lenguajes de programación Simplicidad como regla general. La navaja de Ockham es un principio meto- dológico y filosófico según el cual “en igualdad de condiciones, la explicación más sencilla suele ser la más probable”. En el ámbito de desarrollo de soft- aa ware, cuando dos soluciones generan, potencialmente, el mismo resultado, el más sencillo tendría que prevalecer sobre el otro. Incluso Leonardo Da Vinci afirmó que “la simplicidad representa el máximo nivel de sofisticación”. 1.1.2. Aspectos fundamentales de desarrollo La elección del lenguaje de programación En este punto se incluye una breve descripción de algunos de los lenguajes de programación que más protagonismo han tenido y que más se utilizan en la ac- tualidad [McC04]. El objetivo que se persigue es el de ofrecer al lector una visión general de dichos lenguajes de programación. El listado que se presenta a continua- ción está ordenado alfabéticamente, y no por orden de importancia o relevancia. Ada. Lenguaje de programación de propósito general, orientado a objetos y con un soporte nativo para la construcción de sistemas concurrentes. Desarro- llado originalmente por el Departamento de Defensa de los Estados Unidos, Ada está particularmente pensado para sistemas empotrados y sistemas de tiempo real. Una de las características más relevantes del lenguaje es el de la encapsulación de datos, forzando al programador a elegir entre aquellos ele- mentos que son públicos o privados de cada clase o paquete. Actualmente, el uso principal de Ada está vinculado con los sectores militar y aeroespacial. C. El lenguaje C es considerado como un lenguaje de programación con un nivel de abstracción medio, originalmente asociado al desarrollo del sistema operativo UNIX. Sin embargo, C ofrece características de alto nivel, como las estructuras o las propias sentencias de control incluidas en el lenguaje. C++. Este lenguaje de programación orientado a objetos, compatible con C, ofrece características como el soporte a clases, polimorfismo, plantillas y una biblioteca estándar altamente potente y flexible. Es considerado como el estándar de facto en el ámbito de las aplicaciones gráficas interactivas. C#. Este lenguaje de programación, desarrollado por Microsoft, tiene una sintaxis similar a otros lenguajes populares, como C++ o Java, ofrece me- canismos de alto nivel, como la orientación a objetos, y proporciona herra- mientas de desarrollo especialmente pensadas para plataformas Microsoft. 1.1. Introducción a la programación Ensamblador. Se trata de un lenguaje de bajo nivel caracterizado por es- tablecer una relación directa entre instrucciones máquina y sentencias del propio lenguaje. Precisamente, debido a esta característica, un lenguaje en- samblador siempre estará vinculado inherentemente a un determinado pro- cesador. Con carácter general, no se hará uso de lenguaje ensamblador salvo que existan unas restricciones enormes en lo que se refiere a optimización de código (tiempo de ejecución o tamaño del código). Java. Lenguaje de programación, similar en sintaxis a C y C++, creado por Sun Microsystems y que ofrece un entorno de desarrollo de alto nivel. Una de las principales características del lenguaje es la portabilidad, gracias al con- cepto de Java Virtual Machine y a la posibilidad de convertir el código fuente Java en byte code. Este código se puede ejecutar en cualquier plataforma que tenga disponible una máquina virtual. El auge de Java estuvo principalmente vinculado, en su momento, al desarrollo de aplicaciones web. JavaScript. Lenguaje de scripting interpretado que se ha utilizado principal- mente para el desarrollo de la funcionalidad relativa a la parte del cliente en aplicaciones web. PHP. Lenguaje de scripting que se ha utilizado esencialmente para el desa- rrollo de la parte del servidor en aplicaciones web. Python. Lenguaje de alto nivel, versátil, interpretado y con soporte para la programación orientada a objetos, que se utiliza en un amplio rango de do- minios, desde el desarrollo de la parte del servidor de aplicaciones web hasta el prototipado rápido de componentes de inteligencia artificial. SQL. Lenguaje estándar de referencia en lo que se refiere a consultas, ac- tualización y gestión de bases de datos relacionales. De hecho, SQL es el acrónimo de Structured Query Language. Se trata de un lenguaje declarativo, es decir, que no se basa en el uso explícito de una secuencia de operaciones como tal, sino que está guiado por los resultados de las operaciones que se aplican. Visual Basic. Lenguaje de programación de alto nivel, orientado a objetos, con soporte a la programación visual y que deriva de la versión de BASIC creada para aplicaciones de escritorio en entornos Microsoft. Su acrónimo, BASIC, significa Beginner’s All-purpose Symbolic Instruction Code. Popularidad de los lenguajes de programación. Existen diversos índices que reflejan la demanda actual de los lenguajes de programación más representati- vos. Un ejemplo relevante es el del índice TIOBE, utilizado desde principios de siglo. Capítulo 1 :: Lenguajes de programación En el listado anterior, se ha mencionado el atributo interpretado, asociados al concepto de lenguaje de programación. Esta característica, junto al concepto de lenguaje de programación compilado, serán objeto de estudio más adelante, ya que dichas características resulta especialmente relevante a la hora de escoger un len- guaje de programación para el desarrollo de IA. Convenciones de desarrollo En el desarrollo de software debe existir una relación entre la arquitectura ge- neral propuesta para resolver un problema, materializada mediante el diseño, y la implementación final que le da soporte y que se realiza a través de un lenguaje de programación concreto. Esta coherencia también ha de reflejarse a nivel interno, de forma que la solución a nivel de código sea consistente. Precisamente, esta con- sistencia es la que idealmente ha de derivar en el uso de guías o convenciones de desarrollo a la hora de utilizar, por ejemplo, nombres para las clases, las funciones, las variables e incluso para los comentarios de código. Piense en un proyecto en el que participan, concurrentemente, un equipo de 8 desarrolladores software. Resulta evidente asumir la existencia de una guía que sirva como referencia para escribir un código que sea consistente. De otro mo- do, cada ingeniero utilizaría, por ejemplo, una convención de nombrado diferente para su código. Esta situación dificultaría tanto la coordinación interna del equipo como el futuro mantenimiento del código. Un ejemplo de guía de estilo para el desarrollo de código en C++ es la utilizada internamente por Google1. Otro ejemplo representativo, para el caso del lenguaje de programación Python, es la guía de estilo PEP (Python Enhancement Proposal) 82. Visión general sobre buenas prácticas de desarrollo En este apartado se pretende ofrecer al lector una visión general, y a muy alto nivel, de buenas prácticas de desarrollo cuando se aborda un proyecto. Debido a la profundidad de la temática, a continuación se esbozan algunas de las cuestiones más importantes [McC04]. A nivel de desarrollo de código: Identifique qué aspectos del diseño son susceptibles de abordar de manera previa a la codificación y cuáles se pueden trabajar en el momento de escribir el código. Utilice un convenio de nombrado de acuerdo al lenguaje de programación elegido. 1 https://google.github.io/styleguide/cppguide.html 2 https://www.python.org/dev/peps/pep-0008/ 1.1. Introducción a la programación Figura 1.3: Paradigma de programación en parejas o pair programming. Adopte prácticas de programación que tengan en cuenta cómo se van a ges- tionar posibles errores o excepciones, cuestiones de seguridad de código y diseño de interfaces, entre otras. Evalúe el impacto del uso de tecnologías más maduras o más recientes en el contexto de su proyecto. A nivel de coordinación y trabajo en equipo: Defina un esquema de integración de código, considerando los pasos que han de darse antes de desplegar código en un sistema en producción. Elija una técnica de desarrollo de software que encaje con su filosofía de trabajo, como por ejemplo Pair Programming [BC04] (ver figura 1.3). A nivel de pruebas y calidad: Defina si los programadores usarán un modelo de desarrollo dirigido por tests, como TDD [Bec04], lo cual implica codificar primero las pruebas. Defina si los programadores codificarán pruebas unitarias. Defina si los programadores llevarán a cabo pruebas de integración antes de promocionar su código. Defina si los programadores realizarán una revisión cruzada de código. A nivel de herramientas y entorno de desarrollo: Elija y use un sistema de control de versiones, como Git3. 3 https://git-scm.com/ Capítulo 1 :: Lenguajes de programación Elija lenguaje, versión del lenguaje y versión del compilador (o intérprete) del lenguaje. Elija y valore las ventajas de usar un framework de desarrollo concreto. Identifique y valore herramientas adicionales que puedan facilitar el proce- so de desarrollo, como el depurador, el framework de pruebas o incluso la generación automática de esqueletos de código. Principios de Clean Code Si usted conoce el concepto de deuda técnica, probablemente también le resulte familiar el término clean code. Si no es así, probablemente intuya que una mala decisión en el diseño o incluso en la programación de código puede acarrear con- secuencias que son difíciles de gestionar conforme pasa el tiempo. Implícitamente, ya hemos tratado el concepto de deuda técnica cuando comentamos que la etapa de mantenimiento de un proyecto software es la que suele estar relacionada con la mayor partida económica de un proyecto software a largo plazo. Incluso si se trata de un proyecto personal o de un prototipo interno, la diferencia entre un buen código y un mal código siempre marca la diferencia. Una de las referencias bibliográficas más relevantes en el ámbito del clean es precisamente el libro que lleva su nombre: Clean Code, A Handbook of Agile Soft- ware Craftsmanship [Mar08]. Se anima al lector a revisar los principios fundamen- tales del código limpio y a descubrir la analogía existente entre ellos y la importan- cia de la simplicidad previamente expuesta. Código limpio. La definición de Clean Code puede tener múltiples acep- ciones. Aquí se incluye la traducción de una de ellas, formulada por Bjarne Stroustrup, el creador del lenguaje de programación C++: “Me gusta que mi código sea elegante y eficiente. La lógica del código debería ser sencilla para dificultar la depuración de errores, con un número mínimo de dependencias para facilitar el mantenimiento, con una gestión de errores completa de acuer- do a una estrategia bien definida, y con un rendimiento cercano al óptimo para evitar que otros desarrolladores generen código complejo como consecuencia del primero. El código limpio hace una cosa y la hace bien.” 1.1.3. Lenguajes de programación compilados e interpretados Visión general A la hora de abordar el diseño de una aplicación, tendrá que tomar la decisión de utilizar un lenguaje de programación compilado o interpretado para escribir su código fuente. Como puede imaginar, cada tipo de lenguaje tiene sus fortalezas y debilidades. Esencialmente, la decisión de emplear un lenguaje interpretado esta- rá relacionada con las restricciones de tiempo existentes a la hora de abordar un 1.1. Introducción a la programación proyecto software y la facilidad de realizar futuros cambios en el mismo. La cara negativa de la moneda tiene que ver con el rendimiento. Así, cuando se emplea un lenguaje de programación interpretado, se está incurriendo en unos costes de ejecu- ción elevados para emplear una herramienta que acelera la velocidad de desarrollo. En otras palabras, un lenguaje interpretado puede ser más adecuado para peticiones de desarrollo sobrevenidas, mientras que un lenguaje compilado sería una mejor opción, al menos a priori, para una petición predefinida. Entrando más en detalle, un lenguaje compilado se basa en la escritura de pro- gramas que, una vez compilados, se expresan en instrucciones de una arquitectura máquina concreta. A modo de ejemplo, la operación de suma de dos valores nu- méricos en el lenguaje de programación C++ sería trasladada directamente en la operación ADD en código máquina. Si bien un programa se podría implementar utilizando directamente código máquina, también denominado ejecutable o bina- rio, el nivel de abstracción tan bajo no haría práctico el desarrollo de proyectos complejos. Además, sería necesario realizar una implementación diferente para ca- da arquitectura hardware. Esta es una de las principales razones de la existencia de los compiladores. Un compilador es un programa que traduce código fuente, programado en un lenguaje de programación de alto nivel, en código máquina para una determinada arquitectura. El proceso de compilación se estructura, a su vez, en varios pasos intermedios, tal y como se muestra en la figura 1.4. Se pueden distinguir dos fases principales. La fase de frontend realiza el análisis léxico, sintáctico y semántico de los ficheros de entrada que representan el código fuente del proyecto. La salida de esta fase es un código intermedio independiente de la arquitectura hardware final. La fase de backend, y particularmente el optimizador, toma como entrada dicho código inter- medio y lo mejora mediante diversas estrategias, como la eliminación de código muerto. Posteriormente, el generador de código ofrece como salida el código bina- rio vinculado a una determinada arquitectura, el cual también es optimizado por el generador. Resulta muy común enlazar bibliotecas de código ya existente con el código binario generado por el compilador para aprovecharse de las ventajas de la reutili- zación de código. Así, el enlazador, otro componente de los compiladores moder- nos, posibilita el proceso de enlazado de código a través de dos formas generales: i) estático, donde las dependencias de código se resuelven en tiempo de enlazado, generando un ejecutable autocontenido que no requiere la instalación de bibliotecas externas en la máquina destino, y ii) dinámico, donde las dependencias de código se resuelven en tiempo de ejecución, generando un ejecutable final de menor tamaño pero que requiere la instalación previa de bibliotecas externas. En [VC15] se ofrece una descripción en detalle del proceso de compilación para el caso particular del conjunto de compiladores GCC (GNU Compiler Collection). Por el contrario, un lenguaje interpretado es aquel en el que las instrucciones generadas no se ejecutan directamente sobre una máquina destino, sino que se leen y ejecutan por otro programa, normalmente escrito en el lenguaje de la máquina destino. Si retoma el ejemplo anterior de la suma de dos valores numéricos, en este Capítulo 1 :: Lenguajes de programación Figura 1.4: Fases del proceso de compilación [VC15]. Análisis Parsing Compilación léxico import random 010011011011011 import sys 100101101110010... if argc > 1: print(sys.argv)... else: # Code here...... v = random.randint 010011011011011 (1, 999) 100101101110010 print(v) Bytecode 010011011011011 100101101110010 # More code compilado 010011011011011 100101101110010 Código Código fuente binario Intérprete Máquina virtual Figura 1.5: Visión general de las fases principales de un intérprete Python. caso la misma operación de suma sería reconocida por el intérprete en tiempo de ejecución, el cual la traduciría a una función del tipo add(a, b) y que, posteriormen- te, ejecutaría a través de la instrucción máquina ADD. La figura 1.5 muestra, de manera gráfica, las principales fases abordadas por un intérprete Python. El lenguaje de programación Java. Algunos autores afirman que los len- guajes de programación, en sí mismos, no se pueden clasificar en compilados o interpretados. Son las implementaciones específicas de un lenguaje las que realmente reflejan esta característica. En el caso de Java, están involucrados componentes como la máquina virtual de Java (Java Virtual Machine), compi- ladores nativos como gcj e intérpretes para el propio lenguaje, como BeanShell. Así pues, ¿qué tipo de lenguaje es Java? 1.1. Introducción a la programación Comparativa En primer lugar, es importante destacar que, desde el punto de vista funcional, es posible implementar mediante un lenguaje interpretado cualquier programa que se pueda implementar con un lenguaje compilado, y viceversa. Sin embargo, cada opción, como se ha introducido anteriormente, tiene sus ventajas y desventajas. Estas se resumen a continuación. Con respecto a los lenguajes compilados, Se compila una Generación de código eficiente, que se puede ejecutar un número arbitrario vez para cada de veces, para la máquina destino. En otras palabras, la sobrecarga incurrida máquina. Se por el proceso de compilación, una vez que el código ha sido validado, se ejecuta muchas. reduce a un único proceso. Posibilidad de aplicar optimizaciones durante el proceso de compilación. Incremento del rendimiento con respecto a un lenguaje interpretado. Con respecto a los lenguajes interpretados, Facilidad a la hora de implementar y prototipar código. Con carácter general, reducción de la complejidad a la hora de emplearlos como toma de contacto en relación a un lenguaje compilado. El código se puede ejecutar al vuelo, sin necesidad de un proceso previo de compilación. Teniendo como referencia estas cuestiones, se establecen dos escenarios ge- nerales que pueden servir como referencia a la hora de elegir un lenguaje de programación compilado o interpretado: 1. Si el contexto del proyecto lo permite, un lenguaje interpretado resultaría más adecuado para llevar a cabo tareas de prototipado rápido o para generar una primera versión de un proyecto con agilidad. Como se ha introducido en este capítulo, las aplicaciones de IA en las que se necesiten probar y adap- tar comportamientos con cierta rapidez representan un candidato ideal con respecto a dicha elección. 2. Las tareas más intensas, desde el punto de vista computacional, o aquellas que se ejecuten con más frecuencia dentro de un proyecto software, se po- drían relacionar con un lenguaje compilado. Por el contrario, aquellas menos intensas, como por ejemplo una interfaz de usuario, se podrían relacionar con un lenguaje interpretado. Capítulo 1 :: Lenguajes de programación No todo es blanco o negro. Note que en un mismo proyecto software pueden convivir varios lenguajes de programación. Por ejemplo, se podría utilizar un aa lenguaje compilado para implementar el núcleo funcional del proyecto y un lenguaje interpretado para aquellas cuestiones menos relevantes desde el punto de vista computacional. 1.1.4. Resumen En esta sección se ha ofrecido una visión general del proceso de desarrollo de software, entendido como un proceso complejo que va más allá de la programa- ción. Particularmente, se han agrupado las fases identificadas en cuatro grandes etapas: especificación, ii) diseño, iii) desarrollo y iv) pruebas. Asimismo, se ha es- tablecido la relación de este proceso con dos destrezas fundamentales que deberían interiorizarse por parte de cualquier ingeniero software: i) el uso de metáforas co- mo mecanismo para manejar de manera eficaz la abstracción y ii) la importancia de la simplicidad como eje fundamental para diseñar, programar y mantener código. Esta introducción general ha quedado complementada con una visión general so- bre buenas prácticas de desarrollo y referencias a cuestiones relacionadas con clean code. Por otro lado, se ha ofrecido al lector una comparativa general de lenguajes de programación compilados e interpretados, relacionando la misma con su uso como herramienta para desarrollar proyectos de IA. En la siguiente sección se abordarán las características deseables en un lenguaje de programación para IA. 1.2. Programación de Inteligencia Artificial La programación de aplicaciones de IA comparte la gran mayoría de aspectos y cuestiones a considerar por parte de otro tipo de proyecto donde el desarrollo soft- ware sea el principal protagonista. Sin embargo, es posible identificar un conjunto de características concretas que han de valorarse cuando se aborda un proyecto de IA. Particularmente, en esta sección se discute un listado de 5 características es- pecíficas a tener en cuenta a la hora de elegir un lenguaje de programación. Este listado se complementa con fragmentos de código y con un caso práctico diseñado para ilustrarlas y compararlas cuando se usan los lenguajes de programación C y Python, respectivamente. 1.2. Programación de Inteligencia Artificial 1.2.1. Consideraciones previas En los últimos años, resulta evidente que la transformación digital y la automa- tización han causado una revolución en términos industriales. Uno de los actores cuyo peso ha ido creciendo significativamente es la Inteligencia Artificial. Por lo tanto, la demanda de profesionales que sepan cómo diseñar, desarrollador, validar y mantener proyectos basados en IA se irá incrementando progresivamente en el futuro. Ética en IA. Más allá de la parte técnica vinculada al desarrollo de software e IA, existen otras consideraciones, como la ética, que están inherentemente vinculadas al uso de IA. Este capítulo, no obstante, está centrado en la parte técnica y de desarrollo. En este contexto, a la hora de aprender las competencias necesarias para la programación de software para IA, existen diversos caminos que se podrían recorrer (ver figura 1.6): 1. Aprender a programar software de manera simultánea a conocer los funda- mentos en los que se basa la IA. 2. Aprender a programar software de manera previa a conocer los fundamentos de IA. 3. Aprender los fundamentos de IA de manera previa a abordar los aspectos que conforman el núcleo de la programación actual. Cada opción tiene sus ventajas y desventajas. La primera opción puede resultar demasiado ambiciosa, debido a que sería necesario abordar tanto la complejidad que representa el aprendizaje de la programación, incluyendo la capacidad de pen- sar de forma abstracta, como los fundamentos de las principales técnicas y algo- ritmos de IA. No obstante, si el aprendizaje está guiado por proyectos aplicados y concretos, es posible abordarlo desde un punto de vista incremental. La segunda opción es probablemente la más extendida en gran cantidad de cu- rrículos vinculados al ámbito de Computer Science. En este sentido, disponer de una buena base de programación (particularmente de algoritmia, análisis de com- plejidad y diseño de estructuras de datos), facilita el estudio y entendimiento de los fundamentos en los que se apoyan gran parte de las técnicas de IA existentes. En este sentido, el hecho de disponer de la programación como herramienta lista para usarse en el ámbito de la IA puede simplificar la comprensión y aplicación práctica de dichas técnicas a problemas concretos. Por otro lado, la tercera opción estaría más vinculada con un perfil donde los fundamentos matemáticos pueden tener más presencia, al menos en primera ins- tancia, de forma que el aprendiz domine primero la parte algorítmica y, posterior- mente, aprenda a programar tanto a nivel transversal como a nivel más aplicado a la IA. Capítulo 1 :: Lenguajes de programación 1 1 Proceso de Proceso de aprendizaje aprendizaje de fundamentos 1 de la de Inteligencia programación Proceso unificado Artificial e integral de aprendizaje de programación e Inteligencia 2 2 Artificial Proceso de Proceso de aprendizaje aprendizaje de fundamentos de la de Inteligencia programación Artificial Figura 1.6: Esquema conceptual de los diferentes caminos de aprendizaje a la hora de aunar IA y desarrollo software. Introduction to Machine Learning. Este término es uno de los más buscados cuando una persona decide abordar el aprendizaje automática desde una pers- pectiva de la programación. Le recomiendo que revise y reflexione sobre cómo se aborda en la literatura existente, incluyendo las asignaturas que llevan por título su nombre. A modo de referencia, la asignatura del MIT (Massachusetts Institute of Technology) Introduction to Machine Learning tiene 3 prerrequi- sitos: i) Programación (mencionando específicamente el lenguaje Python), ii) Cálculo y iii) Álgebra Lineal. 1.2.2. Características deseables en un lenguaje de programa- ción para IA En este apartado se describen un conjunto mínimo de propiedades que, ideal- mente, todo lenguaje de programación utilizado para un proyecto de IA debería poseer. No se trata de ofrecer al lector un listado exhaustivo de características, sino una referencia inicial que sirva como punto de partida a la hora de elegir el len- guaje a utilizar para resolver un determinado problema. Para ello, se han escogido las siguientes 5 características: 1. Simplicidad, debido a la importancia de la misma para escribir y mantener código. 2. Capacidad de prototipado rápido, debido a la propia naturaleza de las apli- caciones de IA, que suelen variar considerablemente su implementación en las primeras etapas. 3. Legibilidad, debido a la necesidad de acercar lo máximo posible al progra- mador y al código, considerando que un número significativo de desarrollos en este ámbito parten de pseudocódigo o algoritmos previamente diseñados. 1.2. Programación de Inteligencia Artificial competencias Python Java C++ R C# tiempo Figura 1.7: Curvas de aprendizaje de 5 lenguajes de programación utilizados para programar IA. 4. Existencia de bibliotecas para IA, debido a la importancia de reutilizar código existente que acelere la generación de prototipos. 5. Comunidad de desarrollo, debido a las ventajas que ofrece compartir expe- riencias y soluciones a problemas previamente abordados por otra persona. A continuación, estas características se abordan con más detalle. Simplicidad La importancia de la simplicidad, en un contexto general de desarrollo de soft- ware, ya introdujo en la sección 1.1.1. La simplicidad como características deseable de un lenguaje de programación está relacionada tanto con la curva de aprendizaje necesaria para dominarlo como con la sencillez a la hora de aplicar mecanismos propios del lenguaje. Respecto a la curva de aprendizaje, un lenguaje de programación para IA debería facilitar un nivel básico de productividad incluso para principiantes. Esta capacidad se justifica con la necesidad de probar y validar conceptos de manera ágil, incluso por desarrolladores que no tenga una gran experiencia en el ámbito de la programación. En el dominio de la IA, al menos en las etapas iniciales de desarrollo, el foco debe estar en la técnica o enfoque de IA aplicado, y no tanto en la eficiencia o calidad del código fuente en su versión inicial. No obstante, es deseable que el programador tenga interiorizadas las competencias necesarias para resolver problemas, de manera independiente al lenguaje de programación, y pensar de forma abstracta. La figura 1.7 muestra, de manera gráfica, la curva de aprendizaje de 5 lenguajes vinculados al desarrollo de aplicaciones de IA. Capítulo 1 :: Lenguajes de programación En relación a los mecanismos ofrecidos por el propio lenguaje de manera nativa, es decir, los que forman parte de su estándar, idealmente deberían ser fáci- les de entender y utilizar de manera productiva. Los mecanismos que están diseña- dos para mejorar la productividad de un desarrollador suelen implicar un periodo de tiempo en el que la curva de aprendizaje se hace más pronunciada, ofrecien- do beneficios a más largo plazo. Un ejemplo clásico es el mecanismo de plantillas ofrecido por el lenguaje de programación C++ para manejar la programación gené- rica [Str13], es decir, independiente del tipo de datos que manejen las funciones o las clases. A modo de ejemplo, el lector podría pensar en un algoritmo que funciona para clasificar, automáticamente, tipos de objetos diferentes en base a una función de utilidad. Si bien el beneficio de esta característica resulta evidente, la depuración de código con plantillas introduce una complejidad no desdeñable. El listado 1.1 muestra un ejemplo de uso de plantillas de función en C++. Se invita al lector a probarlo y a introducir, a propósito, un error de compilación. Capacidad de prototipado rápido El desarrollo de aplicaciones de IA suele iniciarse con una fase de prototipado que permita validar un concepto o algoritmo de la forma más ágil posible. En otras palabras, los primeros prototipo software no suelen prestar demasiada atención a la calidad del código o a su rendimiento. Listado 1.1: Ejemplo de función en C++ que hace uso de plantillas 1 #include 2 using namespace std; 3 4 template 5 T GetMax (T a, T b) { 6 T result; 7 result = (a > b) ? a : b; 8 9 return (result); 10 } 11 12 int main () { 13 int i = 5, j = 6, k; 14 long l = 10, m = 5, n; 15 16 k = GetMax(i, j); 17 cout x: 14 return binary_search(vector, low, mid - 1, x) 15 16 # Si no, x solo puede estar en el subarray derecho 17 else: 18 return binary_search(vector, mid + 1, high, x) 19 20 # x no está en el vector de entrada 21 else: 22 return -1 En este sentido, la existencia de bibliotecas o módulos que faciliten la pro- gramación de IA se convierte en una característica deseable a la hora de elegir, o utilizar, un determinado lenguaje de programación. Particularmente, debería prestar especial atención a la existencia de bibliotecas que cubran los siguientes temas: Redes neuronales. Aprendizaje supervisado y no supervisado. Procesamiento natural. Procesamiento de textos. Modelado y caracterización de sistemas expertos. Procesamiento matemático, particularmente estadística y probabilidad. Visión por computador, debido a la relación del análisis y procesamiento de imágenes con la IA. Simuladores y motores de física. 1.2. Programación de Inteligencia Artificial Comunidad de desarrollo La popularidad de un lenguaje es definitivamente una característica que ha de evaluar a la hora de elegir un lenguaje de programación como herramienta para so- lucionar un problema, ya esté relacionado o no con la IA. Si un lenguaje es popular, el número de programadores que lo estén usando será elevado. Cuanto mayor sea el número de usuarios de un lenguaje de programación, mayor será la probabilidad de que alguno de ellos se haya enfrentado a un problema similar al que usted esté afrontando en un momento determinado. El efecto Stack Overflow. En los últimos años han proliferado los sitios web que permiten lanzar preguntas y ofrecer respuestas relacionadas con aspectos del desarrollo de software, desde una perspectiva transversal. Una de las refe- rencias, por excelencia, es Stack Overflow4. Así pues, la existencia de una comunidad de desarrollo amplia y activa es una característica deseable a la hora de elegir un lenguaje de programación para IA. Como ya se introdujo anteriormente en este mismo capítulo, existen índices, como el índice TIOBE, que nos permiten conocer la demanda de los lenguajes de progra- mación actuales, factor que se puede utilizar como posible indicador del nivel de comunidad existente en torno a ellos. Python 30,0% JavaScript 18,5% Go 17,9% TypeScript 17,0% Rust 14,6% Kotlin 12,6% Java 8,8% C++ 8,6% SQL 8,2% C# 7,3% Figura 1.10: Listado de los 10 lenguajes de programación más deseados, de acuerdo a un estudio realizado por StackOverflow, representado en porcentaje de desarrolladores que no están desarrollando con una tecnología concreta pero que han expresado un interés en desarrollar con ella. Fuente: Stack Overflow Developer Survey 2020. Otro aspecto a considerar está relacionado a la existencia de bibliotecas soft- ware que no formen parte del estándar del lenguaje a considerar. En el caso de aplicaciones de IA, la referencia directa a estudiar sería el número de bibliotecas o proyectos activos en dicho dominio. En esencia, es posible inferir que si existe Capítulo 1 :: Lenguajes de programación una comunidad de desarrolladores preocupados por mejorar la funcionalidad aso- ciada a un lenguaje, entonces será más probable que la propia comunidad sea más receptiva a la hora de dar soporte a otros programadores que están comenzando a aprender un lenguaje. Finalmente, otro indicador relevante que se puede estudiar es el número de programadores en activo vinculados a cada lenguaje. No obstante, la popularidad de un lenguaje en un determinado momento temporal puede representar una mejor aproximación a la hora de buscar contenido actualizado. 1.2.3. Caso práctico. Búsqueda de patrones en archivos de texto En este apartado, se discute un caso práctico en el que se pretenden desarro- llar y comparar varias soluciones, utilizando diferentes lenguajes de programación, para resolver un sencillo problema de búsqueda de patrones en archivos de tex- to. Se pretende proporcionar un ejemplo práctico en el que sea posible identificar las características deseables para la programación de IA, previamente discutidas, a la hora de elegir un lenguaje de programación. Aunque se trata de un proble- ma sencillo y con poco alcance, nos servirá como referencia inicial, considerando especialmente las propiedades de simplicidad, capacidad de prototipado rápido y legibilidad. Para caracterizar el caso práctico se asumen los siguientes requisitos funcio- nales: 1. El texto a procesar se encuentra en un archivo de texto. El nombre de este archivo se proporciona por la línea de comandos como primer argumento. 2. El programa debe ser capaz de identificar e imprimir cuántas veces se en- cuentra un determinado patrón en el contenido del archivo previamente men- cionado. El patrón se proporciona por la línea de comandos como segundo argumento. 3. El programa debe ofrecer un control de errores mínimo. Particularmente, si no se proporciona un nombre de archivo o no se especifica el patrón a buscar, entonces el programa debe mostrar un mensaje de error y finalizar. El listado 1.7 muestra una posible implementación en C del problema plantea- do, de acuerdo a los requisitos preestablecidos. 1.2. Programación de Inteligencia Artificial Listado 1.7: Implementación en lenguaje C del programa que busca un patrón de texto 1 #include 2 #include 3 #include 4 #include 5 6 int main (int argc, char *argv[]) { 7 FILE *fp; 8 char *line, *pattern; 9 int occurrences; 10 size_t len; 11 ssize_t read; 12 13 if (argc < 3) { 14 fprintf(stderr, "Error! You need to provide the filepath and the pattern.\n"); 15 fprintf(stderr, "Sinopsis:./02_simple_patterns_error_control \n"); 16 exit(EXIT_FAILURE); 17 } 18 19 pattern = argv; No cuenta las ocurrencias del 20 occurrences = 0; patrón, sino las líneas donde 21 len = 0; aparece el patrón. Si el patrón 22 aparece dos veces en una misma 23 if ((fp = fopen(argv, "r")) == NULL) { línea, solo contará una ocurrencia. 24 fprintf(stderr, "Error opening file %s!\n", argv); 25 exit(EXIT_FAILURE); 26 } 27 28 while ((read = getline(&line, &len, fp)) != -1) { 29 if (strstr(line, pattern) != NULL) { 30 occurrences++; 31 } 32 33 } 34 35 fclose(fp); 36 37 printf("The word %s appeared %d times in the file %s\n", 38 argv, occurrences, argv); 39 40 return 0; 41 } A continuación, se exponen algunas reflexiones con respecto a las caracterís- ticas deseables previamente comentadas: 1. Simplicidad. El código introduce una cierta complejidad inherente a la hora de manejar el lenguaje C, como por ejemplo el uso de punteros (variables que almacenan direcciones de memoria) para gestionar las líneas del archivo, el patrón y la propia línea de comandos. El programador tendría que estar familiarizado con sus fundamentos y con el uso de los operadores unarios * y &. Capítulo 1 :: Lenguajes de programación 2. Capacidad de prototipado rápido. En el caso del lenguaje C, esta caracterís- tica está acoplada al nivel de experiencia del programador. El hecho de uti- lizar un lenguaje compilado, implica la re-compilación del código cada vez que se introduce un nuevo cambio. Adicionalmente, ha sido posible emplear funciones existentes para leer el contenido del archivo y buscar un patrón, respectivamente, pero quizá se echa en falta una mayor agilidad a la hora de procesar archivos y controlar errores. 3. Legibilidad. A nivel global, el código resulta legible y el flujo principal se sigue gracias a los bloques de código definidos. No obstante, la llamada a la función getline() en la línea 28 no ofrece un alto nivel de abstracción. El código relativo al control de errores no está demasiado cerca, desde el punto de vista semántico, de lo que realmente se pretende controlar, es decir, de si se proporcionaron un nombre de archivo y un patrón. Por otro lado, el listado 1.8 muestra una posible implementación en Python del problema planteado, de acuerdo a los requisitos preestablecidos. Listado 1.8: Implementación en Python del programa que busca un patrón de texto 1 #!/usr/bin/python3 2 3 # Import module sys Este sí busca varias 4 import sys ocurrencias en una misma 5 línea, pero solo funciona 6 # Variable to store how many times the pattern was found 7 occurrences = 0 cuando el patrón a buscar 8 es una palabra. 9 try: 10 # Open the file whose name is specified in the first argument 11 with open(sys.argv) as f: 12 for line in f: 13 # The words of every line are stored as a list 14 words = line.split() 15 # Check for matches 16 for w in words: 17 # The pattern to be found is specified in sys.argv 18 if w == sys.argv: 19 occurrences += 1 20 21 print("The word", sys.argv, "appeared", 22 occurrences, "times in the file", sys.argv) 23 f.close() 24 25 except IndexError: 26 print("Error! You need to provide the filepath and the pattern.") 27 print("Sinopsis: python3 02_simple_patterns_error_control ") 1.2. Programación de Inteligencia Artificial A continuación, se exponen algunas reflexiones con respecto a las caracterís- ticas deseables previamente comentadas: 1. Simplicidad. El código es sencillo y compacto, incluso aunque en esta solu- ción se hayan introducido dos bucles for anidados. El número total de líneas es 27, en comparación a las 41 de la solución en lenguaje C. 2. Capacidad de prototipado rápido. En el caso del lenguaje Python, la curva de aprendizaje es asequible, y resulta posible escribir programas como el del listado 1.8 con cierta agilidad. Hubiera sido posible utilizar directamente el intérprete de Python para prototipar el código. 3. Legibilidad. A nivel global, el código resulta muy legible y el flujo principal se sigue gracias a los bloques de código definidos. Note cómo la función de apertura de archivos condiciona, y facilita, la estructura del código. El control de errores mediante bloques try-except también incrementa el nivel semántico en comparación a la solución anterior en C. Con respecto a las características deseables vinculadas a la existencia de bi- bliotecas y a la comunidad de desarrollo, el presente caso práctico no resulta los suficientemente específico para establecer una comparativa más concreta. Por una parte, las bibliotecas utilizadas en las soluciones planteadas forman parte de los es- tándares de los lenguajes C y Python. Por otra parte, resultaría sencillo identificar tanto en libros académicos como en portales web orientados al desarrollo proble- mas similares al aquí abordado. Más allá de C y Python. En este punto, se invita al lector a desarrollar una solución al problema discutido en este apartado, empleando un lenguaje de programación diferente a los aquí utilizados. 1.2.4. Resumen En esta sección se han identificado y discutido 5 características específicas que han de considerarse a la hora de utilizar un lenguaje de programación en el contexto de un proyecto de IA. Estas características son las siguientes: i) simplicidad, ii) ca- pacidad de prototipado rápido, iii) legibilidad, iv) existencia de bibliotecas para IA, y v) comunidad de desarrollo. Esta discusión se ha completado con un caso prácti- co, vinculado a la búsqueda de patrones de texto e implementado en los lenguajes C y Python. Por otro lado, se ha invitado al lector a que reflexione acerca de cómo enfo- car el aprendizaje de los fundamentos de IA y la programación de software. Esta cuestión no ha de estar relacionada directamente con un determinado lenguaje de programación. Capítulo 1 :: Lenguajes de programación 1.3. Lenguajes de programación de IA En la sección 1.2 se discutieron 5 características relevantes a la hora de elegir un lenguaje de programación para IA: simplicidad, capacidad de prototipado rápi- do, legibilidad, existencia de bibliotecas para IA y comunidad de desarrollo. Esta sección pone el foco en dos de los lenguajes de programación más populares en el contexto de aplicaciones de IA, Python y R, al mismo tiempo que se pretende ilus- trar al lector, mediante ejemplos prácticos y código, cómo estos lenguajes hacen gala de las características previamente mencionadas. La sección 1.3.4 discute una solución en Python, concebida de manera incre- mental, para implementar la IA del clásico juego piedra, papel o tijeras. En ella no solo se refleja la potencia y simplicidad de Python como lenguaje para IA, sino que también sirve para mostrar algunas de las características generales del propio lenguaje. 1.3.1. Visión general Tradicionalmente, y como se ha mencionado antes en este capítulo, los lengua- jes de scripting han estado vinculados a la programación de aplicaciones de IA, de- bido a las características previamente expuestas y discutidas. No obstante, algunos lenguajes compilados también se usan de manera extensiva como lenguajes para IA, debido esencialmente a su popularidad, a la existencia de una gran comunidad de desarrolladores con experiencia en dichos lenguajes y a su relevancia histórica. Particularmente, los siguientes lenguajes han cobrado especial relevancia, dentro del contexto de desarrollo de IA, en los últimos años: Python. Probablemente, es el lenguaje de referencia para IA, y que será abor- dado, con más detalle y en el contexto de un ejemplo práctico, en la presente sección. LISP (List Processing). Definido, comúnmente, como el lenguaje para IA más antiguo. Actualmente, se utiliza principalmente para problemas de lógi- ca y de aprendizaje automático. P ROLOG (Logic Programming). Similar a LISP en cuanto a trascendencia y evolución históricas, se utiliza más frecuentemente para procesamiento de lenguaje natural. R. Lenguaje vinculado al desarrollo de IA debido a su facilidad para tra- tar y visualizar datos, combinando con el auge e importancia actual de data science. Haskell. Lenguaje funcional y de tipado estático, que se caracteriza por la importancia en la detección de errores en tiempo de compilación. Esta de- cisión de diseño es uno de los rasgos más relevantes del lenguaje desde su concepción. 1.3. Lenguajes de programación de IA Java. Lenguaje de referencia para multitud de dominios y aplicaciones. De- bido a su importancia histórica y popularidad, siempre será considerado una opción a la hora de desarrollar un proyecto de IA. C++. Se trata, probablemente, de uno de los lenguajes de programación más completo y versátiles en la actualidad. Los motivos de usar C++ como len- guaje para IA son similares a los expuestos para el caso de Java, aunque se debe considerar el incremento notable de rendimiento y las capacidades de desarrollo a medio nivel. 1.3.2. Python como lenguaje de referencia para IA Los lenguajes de programación para IA van y vienen. Sin embargo, Python se mantiene como uno de los principales lenguajes de referencia debido a dos carac- terísticas principales: i) su simplicidad como entorno de programación, y ii) la accesibilidad que ofrece en términos de funcionalidad ya existente. Estas dos ca- racterísticas representan el núcleo de las 5 características deseables de un lenguaje de programación para IA que se han abordado en este libro. Debido a su naturaleza interpretada, es posible instalar y ejecutar Python en las principales plataformas de desarrollo. No existe la necesidad de disponer de un compilador nativo para traducir las instrucciones, ya que el intérprete de Python se encarga de ello. Además, Python es un lenguaje fácil de usar y entender, incluso para personas que no tienen una formación técnica en el ámbito del desarrollo soft- ware. A modo de ejemplo, y desde el punto de vista de la sintaxis del lenguaje, no hay necesidad de puntos y comas constantes, ni de largas declaraciones de tipos, ni de reinventar funciones comunes. Pero, probablemente, una de las ventajas más importantes de usar Python de- riva de su amplio ecosistema funcional. Existe todo un conjunto de bibliotecas externas que facilitan la vida a los desarrolladores de aplicaciones. A la hora de abordar un desarrollo de IA, será prácticamente seguro que ya exista una biblioteca en Python que dé soporte, al menos a nivel general. Fundamentos del lenguaje Python es un lenguaje de programación multi-paradigma, donde la programa- ción estructurada (particularmente procedural) y la POO (Programación Orientada a Objetos) tienen mayor presencia que respecto a la programación funcional. Ade- más, Python es un lenguaje basado en el tipado dinámico, es decir, la comproba- ción de los tipos de datos se hace en tiempo de ejecución. Por otro lado, la gestión de memoria está delegada en un recolector de basura. La biblioteca estándar de Python ofrece un conjunto muy amplio de estructuras de datos y de funcionalidad, que hace que a Python se le asocie el término de batteries included. Capítulo 1 :: Lenguajes de programación A continuación, se incluye un listado de las principales características de Python: Sintaxis elegante, que facilita la escritura y lectura de programas. Facilidad de uso, que convierte a Python en un lenguaje ideal para prototipar y construir soluciones a medida, sin sacrificar el rendimiento. Amplia biblioteca estándar, que da soporte a las tareas de desarrollo más comunes. Lenguaje pegamento, que simplifica la extensión del mismo mediante el desarrollo de nuevos módulos escritos en lenguajes compilados, como C o C++. Modo interactivo, que facilita la prueba de pequeños fragmentos de código. Alta portabilidad, siendo posible ejecutar Python en Windows, Linux, Mac OS X, y Unix. Licencia open-source. Tanto el software de Python como su documenta- ción está licenciado mediante el PSF (Python Software Foundation) License Agreement5 , por lo que es posible usar y modificar Python en proyectos co- merciales. Por otro lado, algunas de las características más destacables, a nivel de lenguaje de programación, son las siguientes: Estructuras de datos variadas y de alto nivel, destacando especialmente las listas y los diccionarios. Tipado fuerte y dinámico, arrojando excepciones cuando en una misma operación se mezclan dos tipos de datos diferentes (por ejemplo, una cadena y un número). Todo es un objeto, lo cual ofrece un enfoque homogéneo y consistente. Soporte para POO, incluyendo herencia múltiple. Características avanzadas, como generators y list comprehensions. Gestión automática de memoria, de forma que el desarrollador se despreo- cupe de la asignación y liberación de memoria. 5 https://docs.python.org/3/license.html#psf-license 1.3. Lenguajes de programación de IA Módulos Python para IA El listado de módulos o bibliotecas que Python ofrece puede resultar abruma- dor. El lector solo tiene que ejecutar la ayuda interactiva del intérprete de Python y teclear modules para comprobarlo: $ python >>> help() Welcome to Python help utility! help> modules En el ámbito particular de bibliotecas Python para IA, destacan los que se expo- nen a continuación, los cuales se han clasificado en grandes categorías vinculadas a las temáticas más populares cuando se aplican técnicas de IA: Redes neuronales. FANN (Fast Artificial Neural Network)6. Biblioteca de redes neurona- les de código abierto, que permite utilizar redes neuronales artificiales multi-capa. Es multi-plataforma y ofrece un entorno de trabajo que fa- cilita el proceso de entrenamiento de la red neuronal. Además de pro- porcionar binding para Python, es posible utilizar FANN con más de una decena de lenguajes de programación. ffnet7. Biblioteca de IA en Python para implementar redes neuronales pre-alimentadas (feed-forward). Los datos de entrenamiento se pueden visualizar mediante una interfaz gráfica de usuario específica. Integra una función de normalización automática de datos, que ahorra tiem- po en la etapa de preprocesamiento. ffnet implementa sus funciones principales en Fortran, aumentando el rendimiento en comparación con soluciones que están implementadas en Python de manera nativa. PyTorch8. Biblioteca optimizada para deep learning que hace uso de GPUs y CPUs. PyTorch se puede entender como un sustituto del paque- te NumPy9 que se aprovecha de la capacidad computacional ofrecidas por las GPUs, en lugar de utilizar CPUs. 6 https://github.com/libfann/fann 7 https://github.com/mrkwjc/ffnet 8 https://pytorch.org/ 9 https://numpy.org/ Capítulo 1 :: Lenguajes de programación Aprendizaje automático. scikit-learn10. Biblioteca de IA en Python, construida sobre las bibliote- cas NumPy, SciPy11 y matplotlib12 , que da soporte a la implementación de algoritmos de aprendizaje automático. Tiene algoritmos incorpora- dos para clasificar objetos, construir regresiones, agrupar objetos simi- lares en conjuntos (clustering), reducir la cantidad de variables aleato- rias, preprocesar datos e incluso elegir el modelo de aprendizaje final. TensorFlow13. Plataforma de código abierto que ofrece mecanismos de soporte para el aprendizaje automático, incluyendo deep learning. Ten- sorFlow ofrece un ecosistema integral y flexible de herramientas, bi- bliotecas y recursos diseñados para crear aplicaciones de machine lear- ning. Aunque los cálculos se pueden expresar en Python, internamente se ejecutan en C++ para incrementar el rendimiento, ahorrando tiempo de ejecución y aumentando la velocidad de los programas desarrollados con esta biblioteca. Keras14. API diseñada con un foco especial en la usabilidad, ofrecien- do mecanismos para reducir la carga cognitiva y APIs consistentes y simples. Uno de sus principales objetivos es el de minimizar el núme- ro de acciones del usuario requeridas para desarrollar los casos de uso comunes. Además, facilita enormente el proceso de depuración, cuenta con una amplia documentación y es uno de los frameworks para deep learning más populares. Procesamiento de lenguaje natural. NLTK (Natural Language Toolkit)15. Biblioteca de propósito general para el procesamiento de texto escrita en Python. Simplifica el procesa- miento lingüístico y ofrece funcionalidad para gestionar la tokenización y el etiquetado de texto. Además, permite la identificación de entidades con nombre e incluso la visualización de árboles de análisis sintáctico. Gensim16. Biblioteca gratuita de código abierto en Python para repre- sentar documentos como vectores semánticos, de una eficiente, en tér- minos de consumo de recursos, y sencilla (para facilitar su procesa- miento). Gensim está diseñado para procesar textos digitales sin es- tructurar (texto plano) utilizando algoritmos de aprendizaje automático no supervisado. 10 https://scikit-learn.org/stable/ 11 https://www.scipy.org/ 12 https://matplotlib.org/ 13 https://www.tensorflow.org/ 14 https://keras.io/ 15 http://www.nltk.org/ 16 https://radimrehurek.com/gensim/ 1.3. Lenguajes de programación de IA spaCy17. Biblioteca gratuita y de código abierto para el procesamiento avanzado del lenguaje natural en Python. spaCy está diseñado especí- ficamente para su uso en producción y ayuda a crear aplicaciones que procesan y comprenden grandes volúmenes de texto. Puede utilizarse para construir sistemas de extracción de información o de comprensión del lenguaje natural, o para preprocesar texto para algoritmos de deep learning. Modelado y caracterización de sistemas expertos. PyCLIPS18. Extensión para el lenguaje Python que incorpora la funcio- nalidad completa de CLIPS19 en aplicaciones de Python. Así, es posible dotar a Python de un motor de inferencia sólido, fiable, ampliamente utilizado y bien documentado. CLIPS representa una de las implemen- taciones de referencia en lo que se refiere a sistemas expertos. Tiene un sistema de inferencia basado en reglas de encadenamiento hacia adelan- te, así como todas las construcciones imperativas y orientadas a objetos, lo cual permite un control total del flujo de ejecución. Experta20. Motor de reglas que empareja un conjunto de hechos con un conjunto de reglas basadas en esos hechos. A continuación, se eje- cutan acciones basadas en estas reglas. Todos los hechos y las reglas son gestionados por el motor de conocimiento, responsable de gene- rar las salidas. Al igual que ocurre con PyCLIPS, Experta también está inspirado en CLIPS. Visión por computador. O PEN CV (Open Source Computer Vision Library)21. Biblioteca de refe- rencia para el desarrollo de aplicaciones de visión computador, la cual ofrece bindings para los lenguajes de programación más populares, in- cluyendo Python. Se puede definir como una biblioteca de software de visión por computador y aprendizaje automático de código abier- to. O PEN CV se construyó para proporcionar una infraestructura común para las aplicaciones de visión por computador y para acelerar el uso de la percepción automática en los productos comerciales. La bibliote- ca cuenta con más de 2.500 algoritmos optimizados, que incluyen un amplio conjunto de algoritmos de visión por ordenador y de aprendi- zaje automático, tanto clásicos como de última generación. Estos algo- ritmos pueden utilizarse para detectar y reconocer rostros, identificar objetos, clasificar acciones humanas en vídeos, seguir los movimientos de la cámara, rastrear objetos en movimiento, extraer modelos 3D de objetos, producir nubes de puntos 3D a partir de cámaras estereoscó- picas, unir imágenes para producir una imagen de alta resolución de 17 https://spacy.io/ 18 http://pyclips.sourceforge.net/web/ 19 http://www.clipsrules.net/ 20 https://pypi.org/project/experta/ 21 https://opencv.org/ Capítulo 1 :: Lenguajes de programación toda una escena, encontrar imágenes similares a partir de una base de datos de imágenes, eliminar los ojos rojos de las imágenes tomadas con flash, seguir los movimientos de los ojos, reconocer paisajes y estable- cer marcadores para superponerlos a la realidad aumentada, entre otras cuestiones. 1.3.3. R: más allá de la estadística R es un lenguaje y un entorno de desarrollo que pone un énfasis especial en dos ámbitos concretos: la estadística y los gráficos22. R ofrece soporte para un amplio abanico de i) técnicas estadísticas, como por ejemplo la modelización lineal y no lineal, pruebas estadísticas clásicas, análisis de series temporales, clasificación, o agrupación, y ii) gráficas. R se caracteriza, además, por ser muy extensible. Uno de los puntos fuertes de R es la facilidad con la que puede generar gráficos de calidad, incluyendo símbolos matemáticos y fórmulas cuando sea necesario. R está disponi- ble como software libre bajo los términos de la licencia GPL (GNU General Public License) de la FSF (Free Software Foundation). Además, R se puede compilar y ejecutar en una amplia variedad de plataformas UNIX, Windows y MacOS. En esta sección se ofrece una introducción general a R, haciendo especial hin- capié en sus principales características como lenguaje orientado al procesamiento y visualización de datos. Figura 1.11: Logo oficial del proyecto R. Fundamentos del lenguaje R se puede definir como un conjunto integrado de programas diseñados para la manipulación de datos, el cálculo y la visualización gráfica. Entre otras cuestio- nes, R se caracteriza por los siguientes aspectos: un eficaz sistema de manipulación y almacenamiento de datos, un conjunto de operadores para el cálculo de arrays, en particular de matrices, una colección amplia, coherente e integrada de herramientas para el análisis de datos, un sistema gráfico para el análisis y la visualización de datos, 22 https://www.r-project.org/ 1.3. Lenguajes de programación de IA un lenguaje de programación bien desarrollado, sencillo y eficaz (basado en el lenguaje S), que incluye sentencias condicionales, bucles, funciones recur- sivas definidas por el usuario y funciones de entrada y salida. El término entorno pretende caracterizar R como un sistema totalmente plani- ficado y coherente, en lugar de una agrupación incremental de herramientas espe- cíficas y con baja flexibilidad, como suele ocurrir con otros programas de análisis de datos. R representa, en gran medida, un vehículo para el desarrollo de nuevos métodos de análisis de datos interactivos. Se ha desarrollado rápidamente y, en los últimos años, se ha ampliado con una gran colección de paquetes. Sin embargo, la mayoría de los programas escritos en R son esencialmente específicos, creados para un único análisis de datos con unas características concretas. ¿Por qué usar R? En este apartado se ofrece al lector un listado acotado de características por las considerar el uso de R, prestando especial atención a perfiles del tipo data analyst, data scientist y business analyst. En este sentido, R es uno de los lenguajes de scripting más demandado en la actualidad en el ámbito data science. Nivel de estandarización. R se considera una herramienta estándar para llevar a cabo tareas estadísticas y de análisis de datos. Capacidad de visualización. R facilita enormemente la generación de gráficos de alta calidad. Captura de datos. R ofrece mecanismos y funcionalidad para simplificar el proceso de captura de datos en múltiples fuentes de información. Portabilidad. R se puede ejecutar en los sistemas operativos más populares en la actualidad. Extensibilidad. R se puede integrar con diversas tecnologías de almacena- miento de datos, y facilita la inclusión de paquetes con funcionalidad especí- fica. Libertad de uso. R está liberado bajo licencia GNU, incrementando su acce- sibilidad y disponibilidad para proyectos de desarrollo. Relevancia en dominios clave. En campos como la sanidad, la banca o el comercio electrónico, entre otros, R representa una de las herramientas de trabajo más populares en la actualidad. Nexo con el aprendizaje automático. R se puede utilizar como base tecnoló- gica para desarrollar soluciones en ámbitos donde técnicas como el análisis predictivo, el análisis de sentimientos u otros métodos de aprendizaje au- tomático tienen cabida. Por ejemplo, Facebook usa R para realizar análisis predictivo y de sentimientos en lo que se refiere a sus usuarios. Capítulo 1 :: Lenguajes de programación Popularidad de R. De acuerdo al índice TIOBE, el lenguaje R alcanzó su pico de popularidad en 2020, el cual ha ido bajando conforme ha pasado el tiempo. Sin embargo, se considera una de las opciones más acertadas en proyectos de IA donde el análisis y la visualización de datos cobran especial importancia. Caso práctico en R: Inferencia difusa y representación gráfica En la sección 1.1.1 se introdujo el concepto de lógica difusa como herramienta para razonar con valores vagos o imprecisos [Zad99], en el contexto del uso de me- táforas que acercan la forma en la que los humanos pensamos a los mecanismos de representación de información que pueden ser utilizados por las máquinas. En este aparatado, se discute un caso práctico que hace uso de la lógica difusa para medir el grado de esfuerzo que un paciente de rehabilitación física necesita aplicar conforme su terapia avanza, de acuerdo a un conjunto de variables de entrada. Esta discusión se materializa con una posible implementación en R. No se pretende ofrecer una discusión detallada de las características de este lenguaje ni de los fundamentos de la lógica difusa. Lo que se persigue, principalmente, es poner de manifiesto la fa- cilidad con la que es posible manejar y representar, visualmente, información con R. Suponga una terapia de rehabilitación física donde un paciente tiene como ob- jetivo recuperar la movilidad en un brazo. El paciente tiene asignada una rutina diaria, compuesta de un número arbitrario de ejercicios que debe realizar. Cada ejercicio tendrá asociado un número de repeticiones. Idealmente, el paciente de- bería ejecutar cada ejercicio de la forma más parecida posible a cómo lo haría su terapeuta. Simplificando, esta relación de parecido tiene una dimensión espacial, de forma que el paciente siga la misma trayectoria que la definida por su terapeuta, y una dimensión temporal, de forma que el paciente realice el ejercicio en un tiem- po razonable. La figura 1.12 muestra una comparación visual de las trayectorias realizadas por un paciente y un terapeuta. Se pretende construir un sistema artificial que sea capaz de monitorizar cómo de adecuada es la ejecución de una rutina de rehabilitación, de forma que sea posible detectar cuándo el paciente se está esforzando en exceso o, por el contrario, con demasiada facilidad. En este último caso, tendría sentido asignarle un ejercicio, o rutina, más ambicioso que fomente la recuperación de movilidad en el miembro a rehabilitar. Para representar tanto las variables que conforman el sistema como su núcleo de inferencia, se opta por utilizar un sistema basado en lógica difusa. En esencia, la lógica difusa permite que una máquina razone gestionando valores imprecisos. El clásico ejemplo que se utiliza cuando se estudia lógica difusa es el de la altura. Por ejemplo, usted usaría, comúnmente, una expresión del tipo “esa persona es muy alta”, en lugar de afirmar “esa persona mide 1,90 metros”. También usaría expre- siones del tipo “si una persona es muy alta, entonces está en buenas condiciones para jugar a baloncesto”. En términos de uso de lógica difusa, la vari