grydra 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gr/core.rb +165 -17
  3. data/lib/gr/version.rb +1 -1
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38aaecb86df2c74c04d634e4591f60332b56cf66054cc5b2a3fd9061c9d50908
4
- data.tar.gz: 2616015ee7528c6d3b19af4f01bcc101134aa741ea4599b22df2dbe83308c8f1
3
+ metadata.gz: 1dd2702f2b578f025ffbfd0976838d42d7b63ac47bd218dfe43575139fbc3915
4
+ data.tar.gz: e93a2abc28eb742ea275dd33a089204ec0eb81ddd49d4dc6d4bcf6ea86f1c4db
5
5
  SHA512:
6
- metadata.gz: a169a960e53bc33a2686ca1657b65c30a1b19a273d190c5e9100dce109f9ddba50a2c6d35c0883a026bb476e4c91768002b7b53f44f33b5a27cfe01850b5e925
7
- data.tar.gz: 27668728857d9ad96f7d375b022c0f39f51fcf0cbb4e7daf145036029d34d052fbe51b669264a332caaac692ac134310193b44c2ea199c533321139156fabd50
6
+ metadata.gz: 0cd0cadf389f66840cdabcbc1bd98bbf333d9abcbff6e87f062833c5bb948a276f50871e9e76d2fa661e8b0ea90f7e049e288ab90c0fce442fc0aae6ac72f866
7
+ data.tar.gz: 3107d188445bf80c541aebaa34cb993203486b69be58dc74c7e9b852debbd91cb0948ed9c567467971f58288c8e002a78af94650e799aa5a1db115cbc7c5a7c5
data/lib/gr/core.rb CHANGED
@@ -143,15 +143,18 @@ module GRYDRA
143
143
  def calcular_salidas(entradas)
144
144
  @neuronas.map { |neurona| neurona.calcular_salida(entradas) }
145
145
  end
146
+
146
147
  end
147
148
 
148
149
  ### CLASE RED NEURONAL ###
149
150
  class RedNeuronal
150
151
  attr_accessor :capas
151
152
 
152
- def initialize(estructura, imprimir_epocas = false, activaciones = nil)
153
+ def initialize(estructura, imprimir_epocas = false, graficar = false, activaciones = nil)
153
154
  @imprimir_epocas = imprimir_epocas
155
+ @graficar = graficar
154
156
  @capas = []
157
+ @hitorial_error = []
155
158
  activaciones ||= Array.new(estructura.size - 1, :tanh)
156
159
  estructura.each_cons(2).with_index do |(entradas, salidas), i|
157
160
  @capas << CapaDensa.new(salidas, entradas, activaciones[i])
@@ -164,7 +167,7 @@ module GRYDRA
164
167
  end
165
168
 
166
169
  # Entrenamiento con mini-batch, early stopping, decay learning rate
167
- def entrenar(datos_entrada, datos_salida, tasa_aprendizaje, epocas, umbral_error = nil, batch_size: 1, paciencia: 5, decay: 0.95)
170
+ def entrenar(datos_entrada, datos_salida, tasa_aprendizaje, epocas, umbral_error = nil, batch_size: 1, paciencia: 50, decay: 0.95)
168
171
  mejor_error = Float::INFINITY
169
172
  contador_paciencia = 0
170
173
 
@@ -228,8 +231,12 @@ module GRYDRA
228
231
 
229
232
  tasa_aprendizaje *= decay
230
233
 
234
+ @hitorial_error << error_total if @graficar
235
+
231
236
  puts "Época #{epoca+1}, Error total: #{error_total.round(6)}, tasa_aprendizaje: #{tasa_aprendizaje.round(6)}" if @imprimir_epocas
237
+
232
238
  end
239
+ GRYDRA.graficar_error(@hitorial_error) if @graficar
233
240
  end
234
241
 
235
242
  def info_red
@@ -268,13 +275,14 @@ module GRYDRA
268
275
  class RedPrincipal
269
276
  attr_accessor :subredes
270
277
 
271
- def initialize(imprimir_epocas = false)
278
+ def initialize(imprimir_epocas = false, graficar = false)
272
279
  @subredes = []
273
280
  @imprimir_epocas = imprimir_epocas
281
+ @graficar = graficar
274
282
  end
275
283
 
276
284
  def agregar_subred(estructura, activaciones=nil)
277
- @subredes << RedNeuronal.new(estructura, @imprimir_epocas, activaciones)
285
+ @subredes << RedNeuronal.new(estructura, @imprimir_epocas, @graficar, activaciones)
278
286
  end
279
287
 
280
288
  def entrenar_subredes(datos, tasa_aprendizaje, epocas, **opts)
@@ -399,8 +407,8 @@ module GRYDRA
399
407
  class FacilRed
400
408
  attr_accessor :red, :vocabulario, :max_valores, :max_valores_salida
401
409
 
402
- def initialize(imprimir_epocas = false)
403
- @red = GRYDRA::RedPrincipal.new(imprimir_epocas)
410
+ def initialize(imprimir_epocas = false, graficar = false)
411
+ @red = GRYDRA::RedPrincipal.new(imprimir_epocas, graficar)
404
412
  @vocabulario = nil
405
413
  @max_valores = {}
406
414
  @max_valores_salida = {}
@@ -617,7 +625,12 @@ module GRYDRA
617
625
  predicciones = red.predecir_texto(nuevos_textos)
618
626
  EX
619
627
  },
620
-
628
+ 'GRYDRA.describe_metodo' => {
629
+ descripcion: 'Muestra ejemplo de instancia de una clase o metodo.',
630
+ ejemplo: <<~EX
631
+ GRYDRA.describe_metodo("GRYDRA", "guardar_modelo")
632
+ EX
633
+ },
621
634
  # Guardar y cargar modelos y vocabularios
622
635
  'GRYDRA.guardar_modelo' => {
623
636
  descripcion: 'Guarda el modelo entrenado en un archivo binario para poder cargarlo después. Opcionalmente guarda también el vocabulario.',
@@ -681,10 +694,47 @@ module GRYDRA
681
694
  EX
682
695
  },
683
696
  'GRYDRA.generar_ejemplo' => {
684
- descripcion: 'Genera un ejemplo con código funcioanl con la libreria, con ejemplos del 1 al 9.',
697
+ descripcion: 'Genera un ejemplo de código funcional con la libreria, con ejemplos del 1 al 9.',
685
698
  ejemplo: <<~EX
686
699
  GRYDRA.generar_ejemplo(1)
687
700
  EX
701
+ },
702
+ 'GRYDRA.sugerir_estructura' => {
703
+ descripcion: 'Sugiere automáticamente una posible estructura de red neuronal según la cantidad de entradas y salidas.',
704
+ ejemplo: <<~EX
705
+ estructura_sugerida = GRYDRA.sugerir_estructura(3, 1) Se pasan las entradas y salidas en numeros y te regresa una estructura que podras usar.
706
+ EX
707
+ },
708
+ 'GRYDRA.convertir_hashes_a_vectores' => {
709
+ descripcion: 'Convierte un arreglo de hashes (tipo JSON) a arreglos numéricos para entrenar.',
710
+ ejemplo: <<~EX
711
+ datos = [
712
+ { nombre: "A", edad: 20, vip: true },
713
+ { nombre: "B", edad: 30, vip: false }
714
+ ]
715
+ datos_vectores = GRYDRA.convertir_hashes_a_vectores(datos, [:edad, :vip])
716
+ EX
717
+ },
718
+ 'GRYDRA.resumen_modelo' => {
719
+ descripcion: 'Muestra por consola las subredes, sus estructuras y funciones de activación de un modelo cargado.',
720
+ ejemplo: <<~EX
721
+ GRYDRA.resumen_modelo(modelo)
722
+ EX
723
+ },
724
+ 'GRYDRA.validar_modelo' => {
725
+ descripcion: 'Verifica si un "modelo" es de verdad un modelo compatible.',
726
+ ejemplo: <<~EX
727
+ GRYDRA.validar_modelo(modelo)
728
+ EX
729
+ },
730
+ 'GRYDRA.probar_todas_normalizaciones' =>{
731
+ descripcion: 'Prueba entrenamiento con :max y :zscore y muestra el error final con cada una.',
732
+ ejemplo: <<~EX
733
+ entradas = [[1], [2], [3]]
734
+ salidas = [[2], [4], [6]]
735
+ estructura = [[1, 3, 1]]
736
+ GRYDRA.probar_todas_normalizaciones(entradas, salidas, estructura)
737
+ EX
688
738
  }
689
739
  }
690
740
 
@@ -693,7 +743,7 @@ module GRYDRA
693
743
  clave = "#{clase}.#{metodo}"
694
744
  info = DESCRIPCIONES_METODOS[clave]
695
745
  if info
696
- puts "\e[1;3;5;41;37mDescripción de #{clave}:"
746
+ puts "\e[1;3;5;37mDescripción de #{clave}:"
697
747
  puts info[:descripcion]
698
748
  puts "\nEjemplo de uso:"
699
749
  puts "#{info[:ejemplo]}\e[0m"
@@ -706,7 +756,7 @@ module GRYDRA
706
756
 
707
757
  # Función que lista todos los métodos públicos documentados en DESCRIPCIONES_METODOS
708
758
  def self.listar_metodos_disponibles
709
- puts "\e[1;3;5;41;37mMétodos públicos documentados:"
759
+ puts "\e[1;3;5;37mMétodos públicos documentados:"
710
760
  grouped = DESCRIPCIONES_METODOS.keys.group_by { |k| k.split('.').first }
711
761
  grouped.each do |clase, metodos|
712
762
  puts " #{clase}:"
@@ -744,7 +794,7 @@ datos_entrenamiento = [
744
794
  ]
745
795
 
746
796
  # Creamos el modelo
747
- modelo = GRYDRA::FacilRed.new(true) # true para que imprima el error cada 1000 épocas
797
+ modelo = GRYDRA::FacilRed.new(true, true) # true para que imprima el error cada 1000 épocas y otro true para graficar
748
798
 
749
799
  # Entrenamos directamente usando datos tipo hash
750
800
  modelo.entrenar_hashes(
@@ -791,7 +841,7 @@ end
791
841
  require 'grydra'
792
842
 
793
843
  # Crear red principal
794
- red = GRYDRA::RedPrincipal.new #Sin el true, simplemente no se imprimen las epocas
844
+ red = GRYDRA::RedPrincipal.new #Sin parametros, simplemente no se imprimen las epocas ni se grafica
795
845
 
796
846
  # Agregar una subred con estructura [2 entradas, 2 ocultas, 1 salida]
797
847
  red.agregar_subred([2, 3, 1], [:tanh, :tanh])
@@ -853,7 +903,7 @@ datos_in_no = GRYDRA.normalizar_varios(datos_in, max_in, :max)
853
903
  datos_ou_no = GRYDRA.normalizar_varios(datos_ou, max_ou, :max)
854
904
 
855
905
  # Crear red principal
856
- red_principal = GRYDRA::RedPrincipal.new
906
+ red_principal = GRYDRA::RedPrincipal.new(false, true) #No imprime errores, pero si grafica
857
907
 
858
908
  # Agregar subredes
859
909
  red_principal.agregar_subred([1, 4, 1], [:sigmoid, :tanh])
@@ -901,7 +951,7 @@ tanto como uno quiera, ejemplo:
901
951
  0.003, 0.3, 0.9, 0.2, 0.223, 0.00008
902
952
  =end
903
953
  # Crear instancia de la clase FacilRed
904
- red = GRYDRA::FacilRed.new(true) # true para imprimir las épocas y errores
954
+ red = GRYDRA::FacilRed.new(true, true) # true para imprimir las épocas y otro true para graficar
905
955
 
906
956
  # Datos originales (temperatura Celsius y Fahrenheit)
907
957
  datos_in = [[0], [10], [20], [30], [40], [50], [-10], [-20], [100], [-30], [-5], [-40]]
@@ -961,7 +1011,7 @@ estructuras = [
961
1011
  ]
962
1012
 
963
1013
  # Crear red usando la interfaz fácil
964
- red = GRYDRA::FacilRed.new(true) # true para que imprima error por época
1014
+ red = GRYDRA::FacilRed.new(true) # true para que imprima solamente error por época
965
1015
 
966
1016
  # Entrenar la red
967
1017
  red.entrenar_numericos(
@@ -1016,7 +1066,7 @@ estructuras = [
1016
1066
  ]
1017
1067
 
1018
1068
  # Crear red Fácil
1019
- red = GRYDRA::FacilRed.new # true para ver el progreso de entrenamiento
1069
+ red = GRYDRA::FacilRed.new(true, true) # true para ver el progreso de entrenamiento y otro true para graficar
1020
1070
 
1021
1071
  # Vamos a asignar activación 'sigmoid' en la última capa para limitar la salida entre 0 y 1
1022
1072
  # Ajustamos internamente en el método agregar_subred
@@ -1140,5 +1190,103 @@ puts "Precio aproximado en dolares es de $\#{prediccion_desnorm.round(2)}"
1140
1190
  puts "Ejemplo generado y guardado en \e[33m#{File.join(path, nombre_archivo)}\e[0m"
1141
1191
  end
1142
1192
  end
1143
- end
1144
1193
 
1194
+ def self.sugerir_estructura(entradas, salidas = 1)
1195
+ ocultas = [(entradas + salidas) * 2, (entradas + salidas)].uniq
1196
+ [[entradas, *ocultas, salidas]]
1197
+ end
1198
+
1199
+ def self.graficar_error(errores, imprimir_cada = 5, ancho_barra = 40, delta_min = 0.001)
1200
+ max_error = errores.max
1201
+ primer_error = errores.first
1202
+
1203
+ puts "\nGráfica de error por época"
1204
+ puts "-" * (ancho_barra + 40)
1205
+
1206
+ ultima_impresa = nil
1207
+
1208
+ errores.each_with_index do |error, i|
1209
+ epoca = i + 1
1210
+ next unless epoca == 1 || epoca == errores.size || epoca % imprimir_cada == 0
1211
+
1212
+ if ultima_impresa && (ultima_impresa - error).abs < delta_min && epoca != errores.size
1213
+ # Si la diferencia con la última impresa es menor al delta, saltamos
1214
+ next
1215
+ end
1216
+
1217
+ largo_barra = [(ancho_barra * error / max_error).round, 1].max
1218
+ barra = "=" * largo_barra
1219
+ mejora_pct = ((primer_error - error) / primer_error.to_f) * 100
1220
+ mejora_str = mejora_pct >= 0 ? "+#{mejora_pct.round(2)}%" : "#{mejora_pct.round(2)}%"
1221
+
1222
+ puts "Época #{epoca.to_s.ljust(4)} | #{barra.ljust(ancho_barra)} | Error: #{error.round(6)} | Mejora: #{mejora_str}"
1223
+
1224
+ ultima_impresa = error
1225
+ end
1226
+
1227
+ puts "-" * (ancho_barra + 40)
1228
+ puts "Error inicial: #{primer_error.round(6)}, Error final: #{errores.last.round(6)}\n\n"
1229
+ end
1230
+
1231
+ def self.convertir_hashes_a_vectores(array_hashes, claves)
1232
+ array_hashes.map do |hash|
1233
+ claves.map { |k| hash[k] ? (hash[k] == true ? 1.0 : hash[k] == false ? 0.0 : hash[k].to_f) : 0.0 }
1234
+ end
1235
+ end
1236
+
1237
+ def self.resumen_modelo(modelo, entrada_prueba = nil)
1238
+ puts "\n\e[1;36mResumen del modelo:\e[0m"
1239
+
1240
+ # Por si es un envoltorio tipo FacilRed
1241
+ if modelo.respond_to?(:red) && modelo.red.respond_to?(:subredes)
1242
+ modelo = modelo.red
1243
+ end
1244
+
1245
+ if modelo.respond_to?(:subredes)
1246
+ modelo.subredes.each_with_index do |subred, i|
1247
+ puts "\n Subred ##{i + 1}:"
1248
+
1249
+ estructura = subred.capas.map { |c| c.neuronas.size }
1250
+ activaciones_ocultas = subred.capas[0...-1].map(&:activacion)
1251
+ funcion_salida = subred.capas.last.activacion
1252
+
1253
+ puts " - Estructura: #{estructura.inspect}"
1254
+ puts " - Activaciones ocultas: #{activaciones_ocultas.inspect}"
1255
+ puts " - Función de salida: #{funcion_salida.inspect}"
1256
+
1257
+ if entrada_prueba
1258
+ begin
1259
+ salida = subred.calcular_salidas(entrada_prueba)
1260
+ puts " - Salida numérica con entrada #{entrada_prueba.inspect}: #{salida.inspect}"
1261
+ rescue => e
1262
+ puts " - Error al calcular salida: #{e.message}"
1263
+ end
1264
+ end
1265
+ end
1266
+ else
1267
+ GRYDRA.validar_modelo(modelo)
1268
+ end
1269
+ end
1270
+
1271
+
1272
+
1273
+ def self.probar_todas_normalizaciones(entradas, salidas, estructuras)
1274
+ [:max, :zscore].each do |tipo|
1275
+ puts "\nProbando normalización: #{tipo}"
1276
+ red = GRYDRA::FacilRed.new(false)
1277
+ error_final = red.entrenar_numericos(entradas, salidas, estructuras, 0.1, 5000, tipo)
1278
+ puts " Error final: #{error_final}"
1279
+ end
1280
+ end
1281
+
1282
+ def self.validar_modelo(modelo)
1283
+ if modelo.nil?
1284
+ puts "\e[31mError: modelo es nil\e[0m"
1285
+ elsif modelo.is_a?(GRYDRA::FacilRed) || modelo.is_a?(GRYDRA::RedPrincipal)
1286
+ puts "\e[32mModelo válido de tipo #{modelo.class}\e[0m"
1287
+ else
1288
+ puts "\e[33mAdvertencia: El modelo cargado no es una instancia conocida (#{modelo.class})\e[0m"
1289
+ end
1290
+ end
1291
+
1292
+ end
data/lib/gr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module GR
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grydra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Razo