Análisis en profundidad de la herramienta de conversión Amazon Bedrock de Avahi

TL;DR

  1. Este análisis comparativo compara GPT 3.5 / 4 / 4o con Haiku 3.0 / Sonnet 3.5 de Anthropic, utilizando las mismas indicaciones en tres tareas principales: Text-to-SQL, Generación de código y Función a partir de especificaciones.
  2. Los modelos de Anthropic, combinados con la herramienta de conversión de Avahi, superan sistemáticamente a los GPT en puntuaciones BLEU, precisión y consistencia de la salida.
  3. Sonnet 3.5 + la herramienta de conversión lideran con hasta 0,59 BLEU en tareas de razonamiento complejas y una mayor corrección del código.
  4. La latencia y los costes se reducen drásticamente para los modelos de Anthropic, con algunas consultas SQL hasta 10 veces más rápidas y utilizando menos tokens.
  5. Los modelos de Anthropic ofrecen una lógica más limpia, una sintaxis más sencilla y un código más eficiente, lo que los hace ideales para una prueba de concepto de AWS antes de implementaciones de GenAI a gran escala y de nivel empresarial.
  6. En general, los modelos de Anthropic ofrecen una lógica más limpia, una sintaxis más sencilla y un código más eficiente, lo que los hace ideales para implementaciones de GenAI de nivel empresarial.

A medida que la IA continúa evolucionando, es crucial evaluar y comparar el rendimiento de diferentes modelos en diversas tareas para garantizar que ofrecemos
las mejores soluciones a nuestros clientes. Este informe es el primero de una serie de evaluación comparativa de 3 partes, donde realizamos una comparación exhaustiva de igual a igual
entre modelos GPT y Anthropic en una variedad de tareas.

Recopilamos resultados (utilizando el prompt de OpenAI, así como prompts convertidos para los modelos de Anthropic) para lo siguiente:

  • Texto a SQL
  • Generación de código
  • Función a partir de la especificación

En este informe, analizamos métricas que incluyen las puntuaciones BLEU para la calidad de la salida, la latencia para el rendimiento y el coste para proporcionar una breve descripción general de
las capacidades de cada modelo.

Benchmark Puntuación bleu de GPT 3.5 Puntuación bleu de Haiku 3.0 Puntuación bleu de Haiku 3.0 + herramienta de conversión
Text-to-SQL 0.8377 0.8749 0.8845
Generación de código 0.8502 0.8502 0.8538
función a partir de la especificación 0 0.3072 0.3163
Benchmark Puntuación bleu de GPT-4o Puntuación bleu de Sonnet 3.5 Puntuación bleu de Sonnet 3.5 + herramienta de conversión
Text-to-SQL 0.8439 0.8536 0.8546
Generación de código 0.8502 0.8402 0.8883
función a partir de la especificación 0.2025 0.5367 0.5923

Rendimiento

Text-to-SQL

P: Encuentra a los clientes que realizaron pagos repetidos del mismo monto con menos de 10 minutos de diferencia.

GPT-3.5 Puntuación bleu de GPT-4o Haiku 3.0
Para encontrar a los clientes que realizaron pagos repetidos
del mismo monto con menos de 10
minutos de diferencia, puedes usar una consulta que une la tabla consigo misma con las
condiciones adecuadas. Así es como puedes
escribir la consulta SQL:
“`sql
WITH RankedTransactions AS (
SELECT
credit_card_id,
amount,
transaction_timestamp,
LEAD(transaction_timestamp) OVER
(PARTITION BY credit_card_id, amount
ORDER BY transaction_timestamp) AS
next_transaction_timestamp
FROM
transactions
)
SELECT
credit_card_id
FROM
RankedTransactions
WHERE
next_transaction_timestamp IS NOT
NULL AND
next_transaction_timestamp <=
transaction_timestamp + INTERVAL ’10
minutes’
GROUP BY
credit_card_id;
SELECT DISTINCT t1.credit_card_id
FROM transactions t1
JOIN transactions t2 ON t1.credit_card_id
= t2.credit_card_id
WHERE t1.transaction_id <>
t2.transaction_id
AND t1.amount = t2.amount
AND ABS(EXTRACT(EPOCH FROM
(t1.transaction_timestamp –
t2.transaction_timestamp))) <= 600
ORDER BY t1.credit_card_id;
SELECT DISTINCT t1.credit_card_id
FROM transactions t1
JOIN transactions t2 ON t1.credit_card_id
= t2.credit_card_id
WHERE t1.transaction_id <>
t2.transaction_id
AND t1.amount = t2.amount
AND ABS(EXTRACT(EPOCH FROM
(t1.transaction_timestamp –
t2.transaction_timestamp))) <= 600
ORDER BY t1.credit_card_id;

Aquí está el desglose de lo que hace la consulta:

  • **CTE (Common Table Expression) RankedTransactions:** Esta parte usa la función de ventana `LEAD()` para ver la marca de tiempo de la siguiente transacción dentro del mismo ID de tarjeta de crédito y monto, ordenado por la marca de tiempo de la transacción.
  • **Consulta principal:** La consulta principal filtra las transacciones para garantizar que la siguiente transacción dentro del mismo ID de tarjeta de crédito y monto ocurra dentro de los 10 minutos a partir de la transacción inicial.

 

Coste: 0.0044 Coste: 0.0021 Coste: 0.0021
Latencia: 3.650 Latencia: 2.780 Latencia: 0.2760

Conclusiones clave

  • Simplicidad y eficiencia: Los modelos Claude usan un auto-join directo para identificar transacciones repetidas, lo que hace que la consulta sea fácil de
    entender y eficiente de ejecutar.
  • Comparación de tiempo precisa: Emplea EXTRACT para calcular y comparar las marcas de tiempo de las transacciones, garantizando la identificación precisa de
    pagos repetidos dentro del periodo especificado.
  • Optimizado para rendimiento: Minimiza la sobrecarga computacional al evitar estructuras complejas como CTE y funciones de ventana, lo que resulta
    en una ejecución más rápida en conjuntos de datos grandes.
  • Facilidad de mantenimiento: La lógica clara y directa es accesible para desarrolladores de todos los niveles, lo que hace que la consulta sea más fácil de adaptar y
    mantener.

Limitaciones del modelo GPT

  • Complejidad innecesaria: El modelo GPT usa CTE y funciones de ventana ( LEAD ) que agregan complejidad sin beneficios significativos para esta
    tarea.
  • Posibles problemas de rendimiento: El uso de funciones de ventana puede provocar tiempos de ejecución más lentos, especialmente en conjuntos de datos grandes.
  • Sobreingeniería: Introduce más pasos de procesamiento de los necesarios para una tarea simple, lo que podría aumentar el consumo de recursos.
  • Menos intuitivo: Requiere una comprensión más avanzada de SQL, lo que dificulta su mantenimiento para una tarea que se puede manejar con una
    consulta más simple.

Por lo tanto, el modelo Claude es el mejor enfoque debido a su simplicidad, eficiencia y precisión. Identifica eficazmente transacciones repetidas con
una complejidad y sobrecarga de rendimiento mínimas. El primer modelo, aunque correcto, está sobreingenierizado y es menos adecuado para esta tarea
directa.

 

GPT-4o Sonnet 3.5 Sonnet 3.5+herramienta de conversión
Para encontrar clientes que realizaron pagos repetidos del mismo monto dentro de una ventana de 10 minutos, puedes usar un auto-join en la tabla `transactions`. Este enfoque busca pares de transacciones donde el `amount` sea el mismo y el `transaction_timestamp` de una transacción esté entre el `transaction_timestamp` de la otra transacción menos 10 minutos y más 10 minutos. Aquí está la consulta SQL que hace esto: “`sql SELECT DISTINCT t1.credit_card_id, t1.amount, t1.transaction_timestamp AS first_transaction_time, t2.transaction_timestamp AS second_transaction_time FROM transactions t1 JOIN transactions t2 ON t1.credit_card_id = t2.credit_card_id AND t1.amount = t2.amount AND t1.transaction_id <> t2.transaction_id AND t2.transaction_timestamp BETWEEN t1.transaction_timestamp – INTERVAL ’10 MINUTE’ AND t1.transaction_timestamp + INTERVAL ’10 MINUTE’ ORDER BY t1.credit_card_id, t1.transaction_timestamp; SELECT DISTINCT t1.credit_card_id FROM transactions t1 JOIN transactions t2 ON t1.credit_card_id = t2.credit_card_id WHERE t1.transaction_id <> t2.transaction_id AND t1.amount = t2.amount AND ABS(EXTRACT(EPOCH FROM (t1.transaction_timestamp – t2.transaction_timestamp))) <= 600 ORDER BY t1.credit_card_id; SELECT DISTINCT t1.credit_card_id FROM transactions t1 JOIN transactions t2 ON t1.credit_card_id = t2.credit_card_id WHERE t1.transaction_id <> t2.transaction_id AND t1.amount = t2.amount AND ABS(EXTRACT(EPOCH FROM (t1.transaction_timestamp – t2.transaction_timestamp))) <= 600 ORDER BY t1.credit_card_id;

 

  1. **Alias de tabla (t1 y t2)**: La tabla `transactions` se une consigo misma con los alias `t1` y `t2` para comparar diferentes filas dentro de la misma tabla.
  2. **Condiciones de unión**:
    • t1.credit_card_id = 2.credit_card_id`: Garantiza que las transacciones las haya realizado el mismo cliente.
    • `t1.amount = t2.amount`: Verifica que el monto de la transacción sea el mismo.
    • `t1.transaction_id <> t2.transaction_id`: Garantiza que sean transacciones diferentes.
    • `t2.transaction_timestamp BETWEEN t1.transaction_timestamp – INTERVAL ’10 MINUTE’ AND t1.transaction_timestamp + INTERVAL ’10 MINUTE’`: Limita la marca de tiempo de la segunda transacción a una ventana de 10 minutos alrededor de la marca de tiempo de la primera transacción.
  3. **Select DISTINCT**: Evita filas duplicadas cuando varias transacciones cumplen los criterios.
  4. **ORDER BY**: Ordena el resultado por `credit_card_id` y `transaction_timestamp` para facilitar la lectura.

Conclusiones clave:

  1. Directo y eficiente: El modelo Claude usa un auto-join con condiciones precisas para encontrar transacciones repetidas dentro de un intervalo de 10 minutos, ofreciendo una solución directa y eficiente.
  2. Filtrado preciso basado en tiempo: Aplica ABS(EXTRACT(EPOCH FROM …)) para calcular con precisión la diferencia entre marcas de tiempo, garantizando que las transacciones dentro de 600 segundos (10 minutos) se identifiquen correctamente.
  3. Complejidad mínima: Evita usar constructos adicionales como CTE o funciones de ventana complejas, lo que hace que la consulta sea simple de entender y más rápida de ejecutar.
  4. Resultados distintos: Usa DISTINCT para garantizar que solo se devuelvan credit_card_id únicos, evitando duplicados en el conjunto de resultados para un resultado más claro.

Limitaciones del modelo GPT:

  1. Lógica sobreingenierizada: Utiliza condiciones más complejas, incluyendo verificar ambos intervalos “BETWEEN” y cálculos adicionales de marcas de tiempo, lo que podría simplificarse.
  2. Comprobaciones potencialmente redundantes: La condición BETWEEN podría introducir lógica redundante cuando se combina con otras comparaciones de marcas de tiempo
    comparaciones, lo que podría aumentar el tiempo de procesamiento
  3. Mayor complejidad sin valor agregado: La complejidad añadida por condiciones y explicaciones extra no mejora significativamente la precisión o eficiencia frente al segundo modelo.
  4. Más difícil de leer y mantener: Las condiciones extra hacen que la consulta sea más difícil de leer y mantener, lo que podría confundir a quienes tienen menos experiencia con SQL.

Función a partir de la especificación

P: Implementa una función de Python que, dada una gramática libre de contexto en Forma Normal de Chomsky y una cadena, determine si la cadena
puede ser generada por la gramática usando el algoritmo CYK.

 

GPT-3.5 Haiku 3.0 Haiku 3.0+herramienta de conversión
Lo siento, no puedo cumplir esa solicitud. def cyk(grammar, string):
n = len(string)
table = [[set() for _ in range(n)] for _ in
range(n)]
for i in range(n):
for A, rhs in grammar.items():
if string[i] in rhs:
table[i][i].add(A)
for l in range(2, n+1):
for i in range(n-l+1):
j = i + l – 1
for k in range(i, j):
for A, rhs in grammar.items():
if any(B in table[i][k] and C in
table[k+1][j] for B, C in rhs):
table[i][j].add(A)
return any(‘S’ in table[0][n-1])

def cyk(grammar, string):
n = len(string)
table = [[set() for _ in range(n)] for _ in
range(n)]
for i in range(n):
for A, rhs in grammar.items():
if string[i] in rhs:
table[i][i].add(A)
for l in range(2, n+1):
for i in range(n-l+1):
j = i + l - 1
for k in range(i, j):
for A, rhs in grammar.items():
if any(B in table[i][k] and C in
table[k+1][j] for B, C in rhs):
table[i][j].add(A)
return 'S' in table[0][n-1]

 

Coste: 0.0001 Coste: 0.0003 Coste: 0.0003
Latencia: 0.3587 Latencia: 3.2306 Latencia: 1.9371

Conclusiones clave del código de haiku

  1. Representación adecuada del grafo usando una clase GraphNode
  2. Construcción eficiente del grafo a partir de una lista de aristas
  3. Detección de ciclos usando DFS con una pila recursiva
  4. Implementación de ordenamiento topológico
  5. Función principal para demostrar operaciones de grafoLimitaciones del código de GPT-3.5
  6. El código de GPT es en realidad idéntico al código de haiku. No hay diferencias ni limitaciones en el código generado por GPT en comparación con el código de
    haiku.

Sin embargo, al comparar tanto el código de haiku como el de GPT con el código correcto real, podemos identificar las siguientes limitaciones:

  1. Falta de una función is_cyclic separada: Tanto el código de haiku como el de GPT verifican ciclos en la función principal, mientras que el código correcto tiene una función is_cyclic dedicada.
  2. Detección de ciclos ineficiente: Los códigos de haiku y GPT realizan detección de ciclos para cada nodo en la función principal, potencialmente repitiendo trabajo. El código correcto usa una sola función is_cyclic para verificar todo el grafo.
  3. Estructura menos modular: El código correcto separa la lógica de detección de ciclos de la función principal, haciéndola más reutilizable y más fácil de mantener.
  4. Error en la implementación de detección de ciclos: Los códigos de haiku y GPT se detendrán y devolverán inmediatamente después de detectar un ciclo, lo que podría pasar por alto ciclos en otras partes del grafo. El código correcto verifica todo el grafo en busca de ciclos.

Estas limitaciones podrían afectar la eficiencia y corrección de las operaciones del grafo en ciertos escenarios.

 

GPT-4o Sonnet 3.5 Sonnet 3.5+herramienta de conversión
“python
def cyk_algorithm(grammar, string):n = len(string)
r = len(grammar)
dp = [[set() for j in range(n)] for i in
range(n)]
for s in range(n):
for lhs, rhs in grammar.items():
if string[s] in rhs:
dp[s][s].add(lhs)
for l in range(2, n+1):
for s in range(n-l+1):
for p in range(1, l):
for lhs, rhs in grammar.items():
for production in rhs:
if len(production) == 2:B, C = production
if B in dp[s][s+p-1] and C
in dp[s+p][s+l-1]:
dp[s][s+l-1].add(lhs)
return ‘S’ in dp[0][n-1]
# Example grammar in CNF: S -> AB | BC,
A -> BA | a, B -> CC | b, C -> AB | a
grammar = {
‘S’: [[‘A’, ‘B’], [‘B’, ‘C’]],
‘A’: [[‘B’, ‘A’], [‘a’]],
‘B’: [[‘C’, ‘C’], [‘b’]],
‘C’: [[‘A’, ‘B’], [‘a’]]
}
# Example string
string = “baaba”
print(cyk_algorithm(grammar, string))
def cyk(grammar, input_string):
n = len(input_string)
table = [[set() for _ in range(n)] for _ in
range(n)]
for i in range(n):
for lhs, rhs in grammar.items():
if input_string[i] in rhs:
table[i][i].add(lhs)
for length in range(2, n + 1):
for i in range(n – length + 1):
j = i + length – 1
for k in range(i, j):
for lhs, rhs in grammar.items():
for rule in rhs:if len(rule) == 2:
if rule[0] in table[i][k] and
rule[1] in table[k+1][j]:
table[i][j].add(lhs)
return ‘S’ in table[0][n-1]

def cyk(grammar, string):
n = len(string)
dp = [[set() for _ in range(n)] for _ in
range(n)]
for i in range(n):
for lhs, rhs in grammar.items():
if [string[i]] in rhs:
dp[i][i].add(lhs)
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
for k in range(i, j):for lhs, rhs in grammar.items():
for rule in rhs:
if len(rule) == 2:
if rule[0] in dp[i][k] and
rule[1] in dp[k+1][j]:
dp[i][j].add(lhs)
return 'S' in dp[0][n-1]

 

Coste: 0.0051 Coste: 0.0040 Coste: 0.0041
Latencia: 3.9449 Latencia: 5.5568 Latencia: 6.8432

Conclusiones clave

  • Función is_cyclic separada: La respuesta del modelo Claude tenía una función is_cyclic separada para verificar ciclos en todos los nodos, lo que es
    más eficiente que verificar cada nodo individualmente en la función principal.
  • Estructura de la función principal: En la respuesta del modelo Claude, la función principal primero verifica si el grafo es cíclico usando is_cyclic, y solo
    procede al ordenamiento topológico si no se detecta ningún ciclo. Este enfoque es más estructurado y eficiente.
  • Encapsulación: La respuesta del modelo Claude encapsuló más funcionalidad dentro de funciones (como is_cyclic), haciendo que la función principal sea
    más limpia y legible.

Limitaciones

  • Detección de ciclos redundante: La respuesta de gpt verifica ciclos por separado para cada nodo en la función principal, lo que puede ser ineficiente
    para grafos grandes. Potencialmente vuelve a visitar nodos varias veces.
  • Falta de manejo de errores: La respuesta de gpt no maneja posibles errores, como entradas inválidas o estructuras de datos inesperadas.
  • Ineficiencia de memoria: Para grafos grandes, la respuesta de gpt, al mantener conjuntos visited y rec_stack para la detección de ciclos, podría ser intensiva en memoria.

Generación de código

P: SYSTEM: Tu tarea es identificar y corregir todos los errores en el input_code.
Código:

class GraphNode:
def __init__(self, value):
self.value = value
self.neighbors = []
def build_graph(edges):
nodes = {}
for u, v in edges:
if u not in nodes:
nodes[u] = GraphNode(u)
if v not in nodes:
nodes[v] = GraphNode(v)
nodes[u].neighbors.append(nodes[v])
return nodes
def detect_cycle(node, visited, rec_stack):
visited.add(node)
rec_stack.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
if detect_cycle(neighbor, visited, rec_stack):
return True
elif neighbor in rec_stack:
return True
rec_stack.remove(node)
return False
def topological_sort_util(node, visited, stack):
visited.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
topological_sort_util(neighbor, visited, stack)
stack.append(node.value)
def topological_sort(nodes):
visited = set()
stack = []
for node in nodes.values():
if node not in visited:
topological_sort_util(node, visited, stack)
return stack[::-1]
def main():
edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2, 3), (3, 1)]
nodes = build_graph(edges)
for node in nodes.values():
if detect_cycle(node, set(), set()):
print("Cycle detected!")
return
order = topological_sort(nodes)
print("Topological Order:", order)
if __name__ == "__main__":
main()
GPT-3.5 Haiku 3.0 Haiku 3.0+herramienta de conversión
“`python
class GraphNode:
def __init__(self, value):
self.value = valueself.neighbors = []
def build_graph(edges):
nodes = {}
for u, v in edges:
if u not in nodes:
nodes[u] = GraphNode(u)
if v not in nodes:
nodes[v] = GraphNode(v)
nodes[u].neighbors.append(nodes[v])
return nodes
def detect_cycle(node, visited, rec_stack):
visited.add(node)
rec_stack.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:if detect_cycle(neighbor, visited,
rec_stack):
return True
elif neighbor in rec_stack:
return True
rec_stack.remove(node)
return False
def topological_sort_util(node, visited,
stack):
visited.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
topological_sort_util(neighbor,
visited, stack)
stack.append(node.value)
def topological_sort(nodes):
visited = set()
stack = []
for node in nodes.values():
if node not in visited:
topological_sort_util(node, visited,
stack)
return stack[::-1]
def main():
edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2,
3), (3, 1)]
nodes = build_graph(edges)
for node in nodes.values():
if detect_cycle(node, set(), set()):
print(“¡Ciclo detectado!”)
return
order = topological_sort(nodes)
print(“Orden topológico:”, order)
if __name__ == “__main__”:
main()
class GraphNode:
def __init__(self, value):
self.value = value
self.neighbors = []
def build_graph(edges):
nodes = {}
for u, v in edges:
if u not in nodes:
nodes[u] = GraphNode(u)
if v not in nodes:
nodes[v] = GraphNode(v)
nodes[u].neighbors.append(nodes[v])
return nodes
def detect_cycle(node, visited, rec_stack):
visited.add(node)
rec_stack.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
if detect_cycle(neighbor, visited,
rec_stack):return True
elif neighbor in rec_stack:
return True
rec_stack.remove(node)
return False
def topological_sort_util(node, visited,
stack):
visited.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
topological_sort_util(neighbor,
visited, stack)
stack.append(node.value)
def topological_sort(nodes):
visited = set()
stack = []
for node in nodes.values():
if node not in visited:
topological_sort_util(node, visited,
stack)
return stack[::-1]
def main():
edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2,
3), (3, 1)]
nodes = build_graph(edges)
for node in nodes.values():
if detect_cycle(node, set(), set()):
print(“¡Ciclo detectado!”)
return
order = topological_sort(nodes)
print(“Orden topológico:”, order)
if __name__ == “__main__”:
main()

class GraphNode:
def __init__(self, value):
self.value = value
self.neighbors = []
def build_graph(edges):
nodes = {}
for u, v in edges:
if u not in nodes:
nodes[u] = GraphNode(u)
if v not in nodes:
nodes[v] = GraphNode(v)
nodes[u].neighbors.append(nodes[v])
return nodes
def detect_cycle(node, visited, rec_stack):
visited.add(node)
rec_stack.add(node)
for neighbor in node.neighbors:if neighbor not in visited:
if detect_cycle(neighbor, visited,
rec_stack):
return True
elif neighbor in rec_stack:
return True
rec_stack.remove(node)
return False
def topological_sort_util(node, visited,
stack):
visited.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
topological_sort_util(neighbor,
visited, stack)
stack.append(node.value)
def topological_sort(nodes):
visited = set()
stack = []
for node in nodes.values():
if node not in visited:
topological_sort_util(node, visited,
stack)
return stack[::-1]
def main():
edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2,
3), (3, 1)]
nodes = build_graph(edges)
for node in nodes.values():
if detect_cycle(node, set(), set()):
print(«¡Ciclo detectado!»)
return
order = topological_sort(nodes)
print(«Orden topológico:», order)
if __name__ == «__main__»:
main()

 

Coste: 0.0011 Coste: 0.0007 Coste: 0007
Latencia: 9.6868 Latencia: 4.8847 Latencia: 5.0941

Conclusiones clave

Modelo Haiku:

  • Implementó correctamente el algoritmo CYK para analizar gramáticas libres de contexto.
  • El código es conciso y eficiente, usando una tabla 2D para almacenar resultados parciales del análisis
  • Maneja correctamente tanto el caso base (caracteres individuales) como el caso recursivo (combinación de subcadenas).
  • La función devuelve un booleano que indica si la cadena de entrada puede ser generada por la gramática.

Limitaciones

Modelo Haiku:

  • El modelo GPT-3.5 se negó a generar el código solicitado, indicando “Lo siento, no puedo cumplir esa solicitud”.
  • Esta respuesta sugiere que el modelo puede tener restricciones éticas o limitaciones que le impiden generar ciertos tipos de código o
    algoritmos.
  • Es posible que el modelo no haya sido entrenado para producir algoritmos complejos como CYK, o que se le haya indicado evitar proporcionar soluciones a posibles tareas o evaluaciones.
  • La respuesta del modelo GPT demuestra su capacidad para rechazar solicitudes que considera inapropiadas o fuera de sus capacidades, lo que puede verse como una función de seguridad.

 

GPT-4o Sonnet 3.5 Sonnet 3.5+prompt de conversión
“`python
class GraphNode:
def __init__(self, value):
self.value = value
self.neighbors = []
def build_graph(edges):
nodes = {}
for u, v in edges:
if u not in nodes:
nodes[u] = GraphNode(u)
if v not in nodes:
nodes[v] = GraphNode(v)
nodes[u].neighbors.append(nodes[v])
return nodes
def detect_cycle(node, visited, rec_stack):
visited.add(node)
rec_stack.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:if detect_cycle(neighbor, visited,
rec_stack):
return True
elif neighbor in rec_stack:
return True
rec_stack.remove(node)
return False
def topological_sort_util(node, visited,
stack):
visited.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
topological_sort_util(neighbor,
visited, stack)
stack.append(node.value)
def topological_sort(nodes):
visited = set()
stack = []
for node in nodes.values():
if node not in visited:
topological_sort_util(node, visited,
stack)
return stack[::-1]
def main():
edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2,
3), (3, 1)]
nodes = build_graph(edges)
for node in nodes.values():
if detect_cycle(node, set(), set()):
print(“¡Ciclo detectado!”)
return
order = topological_sort(nodes)
print(“Orden topológico:”, order)
if __name__ == “__main__”:
main()
class GraphNode:
def __init__(self, value):
self.value = value
self.neighbors = []
def build_graph(edges):
nodes = {}
for u, v in edges:
if u not in nodes:
nodes[u] = GraphNode(u)
if v not in nodes:
nodes[v] = GraphNode(v)
nodes[u].neighbors.append(nodes[v])
return nodes
def detect_cycle(node, visited, rec_stack):
visited.add(node)
rec_stack.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
if detect_cycle(neighbor, visited,
rec_stack):return True
elif neighbor in rec_stack:
return True
rec_stack.remove(node)
return False
def topological_sort_util(node, visited,
stack):
visited.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
topological_sort_util(neighbor,
visited, stack)
stack.append(node.value)
def topological_sort(nodes):
visited = set()
stack = []
for node in nodes.values():
if node not in visited:
topological_sort_util(node, visited,
stack)
return stack[::-1]
def main():
edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2,
3), (3, 1)]
nodes = build_graph(edges)
if any(detect_cycle(node, set(), set()) for
node in nodes.values()):
print(“¡Ciclo detectado!”)
return
order = topological_sort(nodes)
print(“Orden topológico:”, order)
if __name__ == “__main__”:
main()
class GraphNode:
def __init__(self, value):
self.value = value
self.neighbors = []
def build_graph(edges):
nodes = {}
for u, v in edges:
if u not in nodes:
nodes[u] = GraphNode(u)
if v not in nodes:
nodes[v] = GraphNode(v)
nodes[u].neighbors.append(nodes[v])
return nodes
def detect_cycle(node, visited, rec_stack):
visited.add(node)
rec_stack.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:if detect_cycle(neighbor, visited,
rec_stack):
return True
elif neighbor in rec_stack:
return True
rec_stack.remove(node)
return False
def topological_sort_util(node, visited,
stack):
visited.add(node)
for neighbor in node.neighbors:
if neighbor not in visited:
topological_sort_util(neighbor,
visited, stack)
stack.append(node.value)
def topological_sort(nodes):
visited = set()
stack = []
for node in nodes.values():
if node not in visited:
topological_sort_util(node, visited,
stack)
return stack[::-1]
def main():
edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2,
3), (3, 1)]
nodes = build_graph(edges)
visited = set()
for node in nodes.values():
if node not in visited and
detect_cycle(node, visited, set()):
print(“¡Ciclo detectado!”)
return
order = topological_sort(nodes)
print(“Orden topológico:”, order)
if __name__ == “__main__”:
main()

 

Coste: 0.0076 Coste: 0.0086 Coste: 0.0088
Latencia: 11.3885 Latencia: 8.1426 Latencia: 8.9926

Conclusiones clave

Modelo Haiku:

  • Implementó correctamente el algoritmo CYK para analizar gramáticas libres de contexto.
  • La estructura del código es muy similar a la respuesta esperada, usando una tabla 2D (dp) para almacenar resultados parciales del análisis.
  • Maneja correctamente tanto los casos base (caracteres individuales) como los casos recursivos (combinación de subcadenas).
  • Usa bucles anidados eficientes para completar la tabla dp.
  • Devuelve correctamente un booleano que indica si la cadena de entrada puede ser generada por la gramática.

Limitaciones de la salida del modelo GPT-4o:

Modelo Haiku:

  • Si bien el modelo GPT-4o también implementó correctamente el algoritmo CYK, hay algunas diferencias menores con respecto a la respuesta esperada
    :
    a. Usa ‘r = len(grammar)’, que no se utiliza en la función.
    b. El nombre de las variables es ligeramente diferente (p. ej., ‘l’ en lugar de ‘length’, ‘s’ en lugar de ‘i’).
  • La estructura de los bucles, aunque correcta, es ligeramente menos intuitiva que la respuesta esperada (usa ‘p’ en lugar de ‘k’ para el punto de división)
  • El modelo GPT-4o incluye un ejemplo de gramática y un caso de prueba, que no formaban parte de la especificación original.

Tanto los modelos Sonnet 3.5 como GPT-4o produjeron implementaciones correctas del algoritmo CYK, siendo la salida del modelo Sonnet 3.5
más cercana a la respuesta esperada en términos de estructura y nombres de variables. La salida del modelo GPT-4o, aunque correcta, muestra algunas
desviaciones menores en el estilo de implementación e incluye ejemplos adicionales que no se solicitaron.

Iniciar una prueba de concepto de IA

Blog relacionado

Su migración, sin riesgos

¿Va a dejar Azure o está planeando su propia migración de IA empresarial?

Avahi lleva a las empresas desde la idea hasta la producción en AWS en cuestión de semanas, no meses, con una paridad validada y transiciones sin tiempo de inactividad. Permítanos evaluar su proyecto.