Equidad en modelos de machine learning

¿Mi sistema está representando éticamente el problema a solucionar?

Me resultó intrigante una línea de la serie “Dear white people”, en la que un personaje afrodescendiente clama que es importante que esta comunidad haga parte activa del ciclo de desarrollo de software, ya que si no hacen parte de esto el software va a estar sesgado. Esto se ha visto en casos de clasificación de imágenes, donde los países con menores ingresos económicos no tienen resultados de clasificación tan acertados como los de países con mayores ingresos económicos. Esto está estrechamente relacionado con el origen de los datos y las variables que son consideradas para la generación de estos modelos.

Y es que este tipo de implicaciones se ven por ejemplo en NLP, ya que como la mayor parte de investigación está en universidades angloparlantes, encontrar recursos en español es un poco más difícil. Esto además tiene otras implicaciones de carácter ético, como por ejemplo un modelo de clasificación para un sistema jurídico o para préstamos bancarios, ya que son modelos sensibles con respecto a estas poblaciones.

La clasificación de imágenes baja hasta en un 20% en países menos desarrollados

Por esta razón las tres métricas más usadas para medir la equidad, de acuerdo a un estudio realizado en Standford son:

  • Anti-clasificación: Variables como raza o género no deben ser usadas para realizar una clasificación.
  • Clasificación paritaria: Las métricas de desempeño debe ser la misma para las diferentes poblaciones.
  • Calibración: El score generado en un individuo con las ciertas características deben ser las mismas sin importar la población de la cual es obtenido.

Cabe notar que los mismos autores recalcan en los problemas matemáticos que representan estas métricas, ya que se trata de un área poco explorada.

Llevándolo al caso de uso empresarial, se realiza un modelo para predecir si a un cliente se le va a realizar una venta. Para probar que se cumpla la métrica de anti-clasificación al realizarse la creación del modelo se debe revisar que no se hayan usado variables sensibles como raza o género. Normalmente se tiene bastante control sobre esto, pero aún así vale la pena prestarle atención a variables compuestas o a scores generados por otros sistemas, como por ejemplo proveedores de correos.

Para probar que se haga una clasificación paritaria, se separan las poblaciones por estas variables y se les aplican las métricas de desempeño como área bajo la curva ROC, accuracy o F1. También vale destacar que el comparar visualmente las curvas ROC pueden ser de gran ayuda.

Finalmente se puede probar que la calibración se cumpla al tomar una muestra aleatoria y cambiarle los datos en las variables sensibles, y al comparar con la probabilidad de venta original no debería cambiar. Otra herramienta visual que puede generar retroalimentación sobre esta métrica es graficar un histograma de las probabilidades de cierre, y esta distribución debe ser bastante similar entre cada población.

Tutorial básico para publicar un módulo en Pypi

Normalmente el conocimiento base de todo desarrollador llega hasta la solución puntual de un problema, pero cuando este problema resulta siendo bastante común en proyectos propios, del equipo, empresa o ecosistema, entonces es un buen momento para crear una librería. En esta entrada vamos a dar un ejemplo de cómo crear un proyecto con ciertos criterios de calidad, pero al mismo tiempo básico.

En este tutorial vamos a crear una clase Snake en un módulo llamado animals, la cual va a estar documentada con Sphinx y con tests unitarios usando unittest, para ser publicada en pypi.

La clase Snake para este ejemplo va a ser

# snake.py

class Snake:
    def eat(self, animal):
        return 'Delicious!!!' if animal is 'mouse' else 'No thanks'

Estructura de archivos

A continuación vamos a mostrar la estructura propuesta, vamos a ir explicando la finalidad de los demás archivos.

 .
├ animals
|  ├ __init__.py
|  └ snake.py
├ setup.py
├ requirements.txt
├ README.md
└ .gitignore

En el módulo de animals está la clase en el archivo snake.py y un archivo para configurar el módulo, en donde se importan los archivos y clases a usar, como se muestra en el ejemplo

# __init__.py

from .snake import Snake

__name__ = 'animals'
__version__ = '0.0.0'
__all__ = ['Snake']

En el archivo requirements.txt se agregan las dependencias del proyecto, especificando versiones si es necesario, como por ejemplo

# requirements.txt

pandas==0.25.0
sklearn

Este archivo es tomado por el archivo de configuración del módulo, el cual es setup.py, para la configuración de dependencias. Este archivo puede ser de la siguiente forma

# setup.py

import setuptools
import animals

with open('README.md', 'r') as fh:
    long_description = fh.read()

setuptools.setup(
    name='animals',
    version=animals.__version__,
    author='Santa Claus',
    author_email='santa@claus.com',
    description='Please describe this',
    long_description=long_description,
    long_description_content_type='text/markdown',
    url='https://github.com/resuelve/animals',
    packages=setuptools.find_packages(exclude=['sphinx_docs', 'docs', 'tests']),
    python_requires='~=3.5',
    install_requires=[
        i.replace('\n', '')
        for i in open('requirements.txt', 'r').readlines()
    ],
    extras_require={
        'dev': ['setuptools', 'wheel', 'twine', 'Sphinx'],
    },
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Intended Audience :: Developers',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Topic :: Software Development',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
    ],
)

Para terminar, el archivo .gitignore puede ser el mismo generado por github, solo recomendamos que al menos estén estas reglas

# .gitignore

*.egg-info/
build/
dist/
__pycache__/

Pruebas unitarias

Se agregan unas pruebas unitarias usando unittesting. La idea es que sean lo más concisas posibles. Este archivo para este caso va a estar dentro de la carpeta tests.

# test_snake.py

import unittest
from animals.snake import Snake

class TestSnake(unittest.TestCase):

    def test_eat(self):
        snake = Snake()
        self.assertEqual(snake.eat('mouse'), 'Delicious!!!')
        self.assertEqual(snake.eat('elephant'), 'No thanks')


if __name__ == '__main__':
    unittest.main()

La forma de revisar que estén pasando todos los tests es corriendo en consola, el cual solo funciona si los tests están ubicados dentro de la carpeta de tests y comienzan con la palabra test_ , como por ejemplo, test_snake.py

python3 -m unittest tests/test_*

Documentación

Para generar una página web con los comentarios en python nosotros usamos Sphinx. Un ejemplo de la forma de documentar usando la estructura de sphinx se puede ver en la siguiente forma

# snake.py

class Snake:
    ''' Esta es la clase snake, de tener unos parámetros de constructor, acá irían'''
    
    def eat(self, animal):
        ''' Toda serpiente necesita comer
        :param animal: Hoy que animal va a comer
        :type animal: str
        :return: Si la serpiente está dispuesta a comer o no
        :rtype: str
        '''
        return 'Delicious!!!' if animal is 'mouse' else 'No thanks'

Para esto se agrega una carpeta donde se guarde la estructura de los archivos a generar. En este punto la estructura de carpetas debería estar de la siguiente manera

.
├ animals
|  ├ __init__.py
|  └ snake.py
├ tests
|  └ test_snake.py
├ sphinx_docs
|  ├ conf.py
|  ├ index.rst
|  └ snake.rst
├ setup.py
├ requirements.txt
├ MANIFEST.in
├ README.md
└ .gitignore

En conf.py está la configuración y demás formas de revisar el código para generar el HTML, en index.rst está la entrada inicial del proyecto y cómo se va a ver representada y en snake.rst están las directivas de cómo leer el archivo de snake.py

# conf.py

# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('..'))


# -- Project information -----------------------------------------------------

project = 'animals'
copyright = '2019, Resuelve'
author = 'Santa Claus'

# The full version, including alpha/beta/rc tags
release = '0.0.0'


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.viewcode',
    'sphinx.ext.intersphinx',
    'sphinx.ext.autosummary',
]

# Include Python objects as they appear in source files
# Default: alphabetically ('alphabetical')
autodoc_member_order = 'bysource'
# Default flags used by autodoc directives
autodoc_default_flags = ['members', 'show-inheritance']
# This value contains a list of modules to be mocked up
autodoc_mock_imports = [
    # all the dependencies that you want to ignore
]
# Generate autodoc stubs with summaries from code
autosummary_generate = True

# Add any paths that contain templates here, relative to this directory.
# templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# alabater theme opitons
html_theme_options = {
    'github_button': True,
    'github_type': 'star&v=2',
    'github_user': 'resuelve',
    'github_repo': 'silk-ml',
}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named 'default.css' will overwrite the builtin 'default.css'.
html_static_path = ['_static']

# Sidebars configuration for alabaster theme

html_sidebars = {
    '**': [
        'about.html',
        'navigation.html',
        'searchbox.html',
    ]
}

# I don't like links to page reST sources
html_show_sourcelink = True

# Add Python version number to the default address to correctly reference
# the Python standard library
intersphinx_mapping = {'https://docs.python.org/3.7': None}
.. index.rst

*******
animals
*******

.. image:: https://img.shields.io/pypi/v/animals.svg
   :target: https://pypi.python.org/pypi/animals
   :alt: PyPI Version
.. image:: https://img.shields.io/pypi/pyversions/animals.svg
   :target: https://pypi.python.org/pypi/animals
   :alt: PyPI python Version

Simple Intelligent Learning Kit (SILK) for Machine learning

Welcome to silk_ml's documentation!
===================================

:Source code: `github.com project <https://github.com/resuelve/animals>`_
:Bug tracker: `github.com issues <https://github.com/resuelve/animals/issues>`_
:Generated: |today|
:License: MIT
:Version: |release|

Project Modules
===============

List of project's modules

.. toctree::
   :maxdepth: 2
   
   _autosummary/snake.rst

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. snake.rst

snake
========

.. automodule:: animals.snake
   :members:

Generación de binarios

Primero se necesita instalar las herramientas necesarias para poder realizar la compilación del código

python3 -m pip install --upgrade setuptools wheel twine

Ya teniendo esto instalado, primero se compilan y crean los binarios usando el mismo archivo de configuración setup.py, y se suben a pypi usando twine. Vale destacar que primero es necesario haber creado una cuenta en pypi.

python3 setup.py sdist bdist_wheel
python3 -m twine upload dist/*


Facebook
Twitter
YouTube