¿Es GitHub Copilot una bendición o una maldición?

Traducción: https://www.fast.ai/2021/07/19/copilot/

GitHub Copilot es un nuevo servicio de GitHub y OpenAI, descrito como “Su programador de pares de IA”. Es un complemento para Visual Studio Code que genera automáticamente código según el contenido del archivo actual y la ubicación actual del cursor.

Realmente se siente bastante mágico de usar. Por ejemplo, aquí escribí el nombre y la cadena de documentos de una función que debería “ Escribir texto en el archivo fname “:

¡El cuerpo gris de la función ha sido escrito íntegramente para mí por Copilot! Simplemente presiono Taben mi teclado, y la sugerencia es aceptada e insertada en mi código.

Ciertamente, esta no es la primera herramienta de síntesis de programas “impulsada por IA”. La búsqueda de código semántico en lenguaje natural de GitHub en 2018 demostró encontrar ejemplos de código utilizando descripciones sencillas en inglés. Tabnine ha proporcionado finalización de código “impulsada por IA” durante algunos años. La diferencia de Copilot es que puede generar funciones completas de varias líneas e incluso documentación y pruebas, basándose en el contexto completo de un archivo de código.

Esto es particularmente emocionante para nosotros en fast.ai porque promete que puede reducir la barrera de la codificación, lo que nos ayudaría enormemente en nuestra misión . Por lo tanto, estaba particularmente interesado en sumergirme en Copilot. Sin embargo, como veremos, todavía no estoy convencido de que Copilot sea en realidad una bendición. Incluso puede resultar ser una maldición.

Copilot funciona con un modelo de lenguaje de redes neuronales profundas llamado Codex , que se entrenó en repositorios de código público en GitHub. Esto es de particular interés para mí, ya que en 2017 fui la primera persona en demostrar que un modelo de lenguaje de propósito general se puede ajustar para obtener resultados de vanguardia en una amplia gama de problemas de PNL. He desarrollado y que demostraron que, como parte de una lección fast.ai . Sebastian Ruder y yo desarrollamos el enfoque y escribimos un artículo, que fue publicado en 2018 por la Association for Computational Linguistics (ACL). Alec Radford de OpenAI me dijo que este documento lo inspiró a crear GPT, en el que se basa Codex. Este es el momento de esa lección en el que mostré por primera vez que el ajuste fino del modelo de lenguaje brinda un resultado de vanguardia en la clasificación del sentimiento de IMDB:

Un modelo de lenguaje está capacitado para adivinar las palabras que faltan en un fragmento de texto. El enfoque tradicional de “ngram” utilizado en años anteriores no puede hacer un buen trabajo con esto, ya que se requiere contexto para adivinar correctamente. Por ejemplo, considere cómo completaría las palabras que faltan en cada uno de estos ejemplos:

Saber que en un caso “hot day” es correcto, pero en otro que “hot dog” es correcto, requiere leer y (hasta cierto punto) comprender toda la oración. El modelo de lenguaje del Codex aprende a adivinar los símbolos que faltan en el código de programación, por lo que tiene que aprender mucho sobre la estructura y el significado del código de computadora. Como veremos más adelante, los modelos de lenguaje tienen algunas limitaciones importantes que se deben fundamentalmente a cómo se crean.

El hecho de que Copilot esté capacitado en el código disponible públicamente, bajo una variedad de licencias, ha llevado a muchas discusiones sobre las implicaciones éticas y legales . Dado que esto ha sido ampliamente discutido, no voy a profundizar más aquí, aparte de señalar un problema legal claro para los usuarios de Copilot discutido por la abogada de propiedad intelectual Kate Downing, que es que, en algunos casos, el uso de las sugerencias de Copilot puede ser una infracción de licencia (o requiere volver a obtener la licencia de su propio trabajo bajo una licencia compatible con GPL):

Cuanto más compleja y extensa sea la sugerencia, es más probable que tenga algún tipo de expresión con derechos de autor.

Recorrido

Antes de sumergirnos en Copilot más profundamente, repasemos algunos ejemplos más de su uso en la práctica.

Para saber si esa función autogenerada write_textrealmente funciona, necesitamos una prueba. ¡Hagamos que Copilot escriba eso también! En este caso, escribí el nombre de mi función de prueba y Copilot completó la cadena de documentos por mí:

Después de aceptar esa sugerencia, Copilot se confundió un poco y sugirió una función sin sentido que contenía muchas líneas de código casi duplicado:

No hay problema: Copilot puede mostrarnos otras opciones sugeridas presionando Ctrl-Enter. La primera lista en realidad parece bastante razonable (excepto por un carácter de tabulación extra extraño en la primera línea):

Aquí Copilot asume que hay una función disponible llamada read_text, que en realidad no existe. Pero es bastante fácil para nosotros pedirle a Copilot que escriba y pruebe eso para nosotros también.

También pregunté copiloto para crear una función para mí que lo haría “ alquitrán y el directorio opcionalmente compresa dira desten bz2, XZ, o en formato gzip ”, lo que resultó en:

def tar_dir(dir, dest):
"Tar and optionally compress directory `dir` to `dest` in bz2, xz, or gzip format"
if dest.endswith('.bz2'):
with tarfile.open(dest,'w:bz2') as f: f.add(dir, arcname=os.path.basename(dir))
elif dest.endswith('.xz'):
with tarfile.open(dest,'w:xz') as f: f.add(dir, arcname=os.path.basename(dir))
elif dest.endswith('.gz'):
with tarfile.open(dest,'w:gz') as f: f.add(dir, arcname=os.path.basename(dir))
else: raise ValueError('bad dest: {}'.format(dest))

También hice que Copilot creara una prueba utilizando el mismo enfoque básico que el anterior, y escribió:

with TemporaryDirectory() as tmp:
write_text(os.path.join(tmp,'a.txt'),'hello')
write_text(os.path.join(tmp,'b.txt'),'world')
tar_dir(tmp,'out.tar.bz2')
assert os.path.exists('out.tar.bz2')
with tarfile.open('out.tar.bz2') as f:
assert f.getnames()==['a.txt','b.txt']

Esa prueba en realidad no pasa, ya que getnamesen la última línea se incluye el directorio principal, pero es una solución bastante fácil a partir de ahí. Copilot incluso decidió inteligentemente usar la write_textfunción que creé antes, que es algo que no esperaba.

Incluso puedes usar Copilot para escribir prosa. Estoy escribiendo esta publicación de blog en vscode en este momento, y simplemente hice clic en el botón “habilitar Copiloto”. Después de escribir la oración anterior, esto es lo que Copilot recomendó como finalización:

Ahora puedo escribir la publicación de mi blog en una sola línea de texto y Copilot generará el resto de la publicación por mí

¡Claramente Copilot tiene una comprensión bastante exagerada de sus propias capacidades de generación de prosa!

Problemas de código

El código que escribe Copilot no es muy bueno. Por ejemplo, considere la tar_dirfunción anterior. Hay mucho código duplicado allí, lo que significa más código para mantener en el futuro y más código para que el lector lo entienda. Además, la cadena de documentos decía "comprimir opcionalmente", pero el código generado siempre se comprime. Podríamos solucionar estos problemas escribiéndolo de esta manera en su lugar:

def tar_dir(dir, dest):
"Tar and optionally compress directory `dir` to `dest` in bz2, xz, or gzip format"
suf = ':' + Path(dest).suffix[1:]
if suf==':tar': suf=''
with tarfile.open(dest,f'w{suf}') as f: f.add(dir, arcname=dir)

Un problema mayor es que ambos write_texty tar_dirno deberían haberse escrito en absoluto, ya que la funcionalidad para ambos ya la proporciona la biblioteca estándar de Python (como pathlib's write_texty shutil's make_archive). Las versiones de la biblioteca estándar también son mejores, ya que pathlib write_textrealiza una verificación adicional de errores y admite la codificación de texto y el manejo de errores, y make_archiveadmite archivos zip y cualquier otro formato de archivo que registre.

Por qué Copilot escribe código incorrecto

Según el artículo de OpenAI, el Codex solo da la respuesta correcta el 29% del tiempo. Y, como hemos visto, el código que escribe generalmente está mal refactorizado y no aprovecha al máximo las soluciones existentes (incluso cuando están en la biblioteca estándar de Python).

Copilot ha leído todo el archivo de código público de GitHub, que consta de decenas de millones de repositorios, incluido el código de muchos de los mejores programadores del mundo. Dado esto, ¿por qué Copilot escribe un código tan cutre?

La razón es por cómo funcionan los modelos de lenguaje. Muestran cómo, en promedio, la mayoría de la gente escribe. No tienen ningún sentido de lo que es correcto o bueno. La mayoría del código en GitHub es (según los estándares de software) bastante antiguo y (por definición) escrito por programadores promedio. Copilot escupe su mejor conjetura en cuanto a lo que esos programadores podrían escribir si estuvieran escribiendo el mismo archivo que usted. OpenAI discute esto en su documento del Codex:

Al igual que con otros grandes modelos de lenguaje entrenados en un objetivo de predicción del próximo token, el Codex generará un código que sea lo más similar posible a su distribución de entrenamiento. Una consecuencia de esto es que dichos modelos pueden hacer cosas que no son útiles para el usuario

Una forma importante en la que Copilot es peor que los programadores promedio es que ni siquiera intenta compilar el código o verificar que funcione o considerar si realmente hace lo que los docs dicen que debería hacer. Además, Codex no recibió capacitación sobre el código creado en el último año o dos, por lo que carece por completo de versiones recientes, bibliotecas y características del lenguaje. Por ejemplo, pedirle que cree código fastai solo da como resultado propuestas que usan la API v1, en lugar de la v2, que se lanzó hace aproximadamente un año.

Quejarse de la calidad del código escrito por Copilot se siente un poco como encontrarse con un perro que habla y quejarse de su dicción. ¡El hecho de que esté hablando es bastante impresionante!

Seamos claros: el hecho de que Copilot (y Codex) escriban un código de apariencia razonable es un logro asombroso. Desde el punto de vista de la investigación del aprendizaje automático y la síntesis del lenguaje, es un gran paso adelante.

Pero también debemos tener claro que el código de apariencia razonable que no funciona, no verifica los casos extremos, usa métodos obsoletos, es detallado y genera deudas técnicas, puede ser un gran problema.

Los problemas con el código generado automáticamente

Las herramientas de creación de código han existido casi desde que existe el código. Y han sido controvertidos a lo largo de su historia.

La mayor parte del tiempo de codificación no se dedica a escribir código , sino a diseñar, depurar y mantener el código . Cuando el código se genera automáticamente, es fácil terminar con mucho más. Eso no es necesariamente un problema, si todo lo que tiene que hacer para mantenerlo o depurarlo es modificar la fuente desde la que se genera automáticamente el código, como cuando se utilizan herramientas de plantilla de código. Incluso entonces, las cosas pueden volverse confusas durante la depuración, ya que el depurador y los seguimientos de la pila generalmente apuntarán al código detallado generado, no a la fuente de la plantilla.

Con Copilot, no tenemos ninguna de estas ventajas. Casi siempre tenemos que modificar el código que se crea, y si queremos cambiar su funcionamiento, no podemos simplemente volver atrás y cambiar el indicador. Tenemos que depurar el código generado directamente.

Como regla general, menos código significa menos para mantener y comprender. El código de Copilot es detallado, y es tan fácil generar mucho que es probable que termine con mucho código.

Python tiene ricas características dinámicas y de metaprogramación que reducen en gran medida la necesidad de generar código. He escuchado a varios programadores decir que les gusta que Copilot escriba muchos textos estándar para usted. Sin embargo, de todos modos, casi nunca escribo un texto estándar; en cualquier momento en el pasado que necesitaba un texto estándar, usé Python dinámico para refactorizar el texto estándar, por lo que no tuve que escribirlo ni generarlo más. Por ejemplo, en ghapi usé Python dinámico para crear una interfaz completa para la API completa de GitHub en un paquete que pesa solo 40kB (en comparación, un paquete equivalente en Go contiene más de 100,000 líneas de código, la mayoría de ellas generadas automáticamente) .

Un ejemplo muy instructivo es lo que sucedió cuando le pedí a Copilot:

def finetune(folder, model):
"""fine tune pytorch model using images from folder and report results on validation set"""

¡Con una cantidad muy pequeña de escritura adicional, generó estas 89 líneas de código casi completamente automáticamente! En cierto sentido, eso es realmente impresionante. De hecho, básicamente hace lo que se solicitó: ajustar un modelo de PyTorch.

Sin embargo, afina mal el modelo. Este modelo se entrenará lentamente y dará como resultado una precisión deficiente. El ajuste fino de un modelo requiere considerar aspectos como el manejo de estadísticas de la capa por lotes, ajustar el cabezal del modelo antes que el cuerpo, elegir una tasa de aprendizaje correctamente, utilizar un programa de recocido adecuado, etc. Además, probablemente queramos usar entrenamiento de precisión mixto en cualquier GPU CUDA creada en los últimos años, y es probable que queramos agregar mejores métodos de aumento como MixUp. Arreglar el código para agregarlos requeriría muchos cientos de líneas más de código y mucha experiencia en aprendizaje profundo, o el uso de una API de nivel superior como fastai , que puede ajustar un modelo PyTorch en 4 líneas de código, lo que resulta en algo con mayor precisión, más rápido y que sea más extensible).

No estoy seguro de qué sería lo mejor para Copilot en esta situación. No creo que lo que está haciendo ahora sea realmente útil en la práctica, aunque es una demostración de aspecto impresionante.

Analizar Python con una expresión regular

Le pedí a la comunidad de fast.ai ejemplos de ocasiones en las que Copilot había sido útil para escribir código para ellos. Una persona me dijo que lo encontraban invaluable cuando estaban escribiendo una expresión regular para extraer comentarios de una cadena que contenía código Python (ya que querían asignar cada nombre de parámetro en una función a su comentario). Decidí probar esto por mí mismo. Aquí está el mensaje para Copilot:

code_str = """def connect(
host:str, # host to connect to
port:int=80, # port to connect to
ssl:bool=True, # whether to use SSL
) -> socket.socket: # the connected socket
"""
# regex to extract comments from strings looking like code_str

Aquí está el código generado:

comment_re = re.compile(r'^\s*#.*$', re.MULTILINE)

Este código no funciona, ya que el ^carácter vincula incorrectamente la coincidencia al inicio de la línea. En realidad, tampoco captura el comentario, ya que le falta ningún grupo de captura. (La segunda sugerencia de Copilot elimina correctamente el ^personaje, pero aún no incluye al grupo de captura).

Sin embargo, estos son problemas menores en comparación con el gran problema con este código, que es que una expresión regular no puede analizar los comentarios de Python correctamente. Por ejemplo, este sería un fracaso, ya que el #en tag_prefix:str="#"se analiza de forma incorrecta como el inicio de un comentario:

code_str = """def find_tags(
input_str:str, # the string to search for tags
tag_prefix:str="#" # prefix marking the start of a tag
) -> List[str]: # list of all tags found

Resulta que no es posible analizar correctamente el código Python utilizando expresiones regulares. Pero Copilot hizo lo que le pedimos: en el comentario rápido pedimos explícitamente una expresión regular, y eso es lo que Copilot nos dio. El miembro de la comunidad que proporcionó este ejemplo hizo exactamente eso cuando escribió su código, ya que asumió que una expresión regular era la forma correcta de resolver este problema. (Aunque incluso cuando intenté eliminar “regex to” del mensaje, Copilot todavía me pidió que usara una solución de expresiones regulares.) El problema en este caso no es realmente que Copilot esté haciendo algo mal , es que lo que está diseñado para hacer podría no ser lo que es mejor para el programador.

GitHub comercializa Copilot como un “programador de pares”. Pero no estoy seguro de que esto realmente capte lo que está haciendo. Un buen programador de pares es alguien que le ayuda a cuestionar sus suposiciones, identificar problemas ocultos y ver el panorama general. Copilot no hace ninguna de esas cosas, todo lo contrario, asume ciegamente que sus suposiciones son apropiadas y se enfoca completamente en producir código basado en el contexto inmediato de dónde está su cursor de texto en este momento.

Programación de par de IA y sesgo cognitivo

Un programador de pares de IA necesita trabajar bien con humanos. Y viceversa. Sin embargo, los humanos tienen dos sesgos cognitivos en particular que dificultan esto: el sesgo de automatización y el sesgo de anclaje . Gracias a este par de debilidades humanas, todos tendremos la tendencia a confiar demasiado en las propuestas de Copilot, incluso si tratamos explícitamente de no hacerlo.

Wikipedia describe el sesgo de automatización como:

La propensión de los humanos a favorecer las sugerencias de los sistemas automatizados de toma de decisiones y a ignorar la información contradictoria elaborada sin automatización, incluso si es correcta

El sesgo de automatización ya se reconoce como un problema importante en la atención médica , donde los sistemas informáticos de apoyo a la toma de decisiones se utilizan ampliamente. También hay muchos ejemplos en las comunidades judicial y policial, como el funcionario de la ciudad de California que describió incorrectamente una herramienta de IBM Watson utilizada para la vigilancia predictiva: “Con el aprendizaje automático, con la automatización, hay un 99% de éxito, por lo que el robot es … tendrá una precisión del 99% al decirnos lo que va a pasar a continuación ”, lo que llevó al alcalde de la ciudad a decir“ Bueno, ¿por qué no montamos calibres .50 [ahí fuera]? ” (Afirmó que estaba “bromeando”). Este tipo de creencia inflada sobre las capacidades de la IA también puede afectar a los usuarios de Copilot, especialmente a los programadores que no confían en sus propias capacidades.

El laboratorio de decisiones describe el sesgo de anclaje como:

Un sesgo cognitivo que nos hace depender demasiado de la primera información que recibimos sobre un tema.

El sesgo de anclaje ha sido ampliamente documentado y se enseña en muchas escuelas de negocios como una herramienta útil, como en la negociación y la fijación de precios .

Cuando escribimos en vscode, Copilot interviene y sugiere finalizaciones de código de forma totalmente automática y sin ninguna interacción de nuestra parte. Eso a menudo significa que antes de que realmente hayamos tenido la oportunidad de pensar hacia dónde nos dirigimos, Copilot ya ha trazado un camino para nosotros. No solo es entonces la “ primera pieza de información “ que estamos obteniendo, sino que también es un ejemplo de “ sugerencias de los sistemas automatizados de toma de decisiones “: ¡estamos recibiendo un doble golpe de sesgos cognitivos que superar! Y no solo sucede una vez, sino que cada vez que escribimos solo unas pocas palabras más en nuestro editor de texto.

Desafortunadamente, una de las cosas que sabemos sobre los sesgos cognitivos es que el simple hecho de ser consciente de ellos no es suficiente para evitar ser engañados por ellos. Por lo tanto, esto no es algo que GitHub pueda solucionar simplemente mediante una presentación cuidadosa de las sugerencias de Copilot y la educación del usuario.

Ejemplos de uso de Stack Overflow, Google y API

Generalmente, si un programador no sabe cómo hacer algo y no está usando Copilot, lo buscará en Google. Por ejemplo, el codificador que discutimos anteriormente que quería encontrar parámetros y comentarios en una cadena que contiene código podría buscar algo como: “ Python extraer lista de parámetros del código regex “. El segundo resultado de esta búsqueda es una publicación de Stack Overflow con una respuesta aceptada que dice correctamente que no se puede hacer con expresiones regulares de Python. En cambio, la respuesta sugirió usar un analizador como pyparsing . Luego intenté buscar “ pyparsing python comments “ y descubrí que este módulo resuelve nuestro problema exacto .

También intenté buscar “ extraer comentarios del archivo python”, lo que dio un primer resultado que mostraba cómo resolver el problema utilizando el módulo tokenize de la biblioteca estándar de Python . En este caso, el solicitante presentó su problema diciendo “ Estoy tratando de escribir un programa para extraer comentarios en el código que ingresa el usuario. Traté de usar expresiones regulares, pero me resultó difícil escribir. * ”¡Suena familiar!

Esto me tomó un par de minutos más que encontrar un mensaje para Copilot que dio una respuesta, pero resultó en que aprendí mucho más sobre el problema y el posible espacio de soluciones. Las discusiones de Stack Overflow me ayudaron a comprender los desafíos de lidiar con cadenas entre comillas en Python y también explicaron las limitaciones del motor de expresiones regulares de Python.

En este caso, sentí que el enfoque de Copilot sería peor tanto para los programadores experimentados como para los principiantes. Los programadores experimentados tendrían que dedicar tiempo a estudiar las diversas opciones propuestas, reconocer que no resuelven correctamente el problema y, de todos modos, tendrían que buscar soluciones en línea. Los programadores principiantes probablemente sentirían que han resuelto el problema, que en realidad no aprenderían lo que necesitan entender sobre las limitaciones y capacidades de las expresiones regulares y terminarían con un código roto sin siquiera darse cuenta.

Además de CoPilot, Microsoft, los propietarios de GitHub, han creado un producto diferente pero relacionado llamado “ Ejemplos de uso de API “. Aquí hay un ejemplo tomado directamente de su sitio web:

Esta herramienta busca ejemplos en línea de personas que usan la API o la biblioteca con la que está trabajando y proporcionará ejemplos de código real que muestran cómo se usa, junto con enlaces a la fuente del ejemplo. Este es un enfoque interesante que se encuentra en algún lugar entre Stack Overflow (pero pierde las valiosas discusiones) y Copilot (pero no proporciona propuestas personalizadas para su contexto de código particular). La pieza adicional crucial aquí es que se vincula a la fuente. Eso significa que el codificador puede ver el contexto completo de cómo otras personas están usando esa función. Las mejores formas de mejorar la codificación son leer y escribir código. Ayudar a los programadores a encontrar código relevante para leer parece ser un enfoque excelente tanto para resolver los problemas de las personas como para ayudarlas a mejorar sus habilidades.

Si la función de ejemplos de uso de API de Microsoft resulta ser excelente, realmente dependerá de su capacidad para clasificar el código por calidad y mostrar los mejores ejemplos de uso. Según el gerente de producto (en Twitter), esto es algo en lo que están trabajando actualmente.

Conclusiones

Sigo sin saber la respuesta a la pregunta del título de esta publicación: “¿GitHub Copilot es una bendición o una maldición?” Podría ser una bendición para algunos y una maldición para otros. Para aquellos para quienes es una maldición, es posible que no lo descubran durante años, porque la maldición sería que aprenden menos, aprenden más lento, aumentan la deuda técnica e introducen errores sutiles, son cosas que es posible que no se den cuenta. , especialmente para los desarrolladores más nuevos.

Copilot podría ser más útil para lenguajes que son muy populares y tienen una funcionalidad de metaprogramación limitada, como Go. (Mucha gente hoy en día usa la generación de código con plantilla con Go por esta razón). Otra área para la que puede ser particularmente adecuada son los programadores experimentados que trabajan en lenguajes desconocidos, ya que puede ayudar a obtener la sintaxis básica correcta y apuntar a funciones de biblioteca y funciones comunes. modismos.

Lo que hay que recordar es que Copilot es una vista previa de una tecnología muy nueva que mejorará cada vez más. Habrá muchos competidores apareciendo en los próximos meses y años, y GitHub sin duda lanzará nuevas y mejores versiones de su propia herramienta.

Para ver mejoras reales en la síntesis de programas, necesitaremos ir más allá de los modelos de lenguaje, a una solución más holística que incorpore las mejores prácticas en torno a la interacción persona-computadora, ingeniería de software, pruebas y muchas otras disciplinas. Actualmente, Copilot se siente como un producto diseñado e implementado por investigadores de aprendizaje automático, en lugar de una solución completa que incorpora toda la experiencia de dominio necesaria. Estoy seguro de que eso cambiará.

bootcampai.org