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.
- checksums.yaml +4 -4
- data/lib/gr/core.rb +165 -17
- data/lib/gr/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1dd2702f2b578f025ffbfd0976838d42d7b63ac47bd218dfe43575139fbc3915
|
|
4
|
+
data.tar.gz: e93a2abc28eb742ea275dd33a089204ec0eb81ddd49d4dc6d4bcf6ea86f1c4db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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:
|
|
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
|
|
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;
|
|
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;
|
|
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
|
|
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
|
|
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