Gradient descent training method for FCM_FTS
This commit is contained in:
parent
e7d603015a
commit
2abc070f1d
@ -1,18 +1,39 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
def step(x):
|
def step(x, deriv=False):
|
||||||
if x <= 0:
|
if deriv:
|
||||||
return 0
|
1 * (x == 0)
|
||||||
else:
|
else:
|
||||||
return 1
|
return 1 * (x > 0)
|
||||||
|
|
||||||
|
|
||||||
def sigmoid(x):
|
def sigmoid(x, deriv=False):
|
||||||
return 1 / (1 + np.exp(-x))
|
if deriv:
|
||||||
|
#return sigmoid(x)*(1 - sigmoid(x))
|
||||||
|
return x * (1 - x)
|
||||||
|
else:
|
||||||
|
return 1 / (1 + np.exp(-x))
|
||||||
|
|
||||||
|
|
||||||
def softmax(x):
|
def softmax(x, deriv=False):
|
||||||
mvs = sum([np.exp(k) for k in x.flatten()])
|
if deriv:
|
||||||
return np.array([np.exp(k)/mvs for k in x.flatten()])
|
pass
|
||||||
|
else:
|
||||||
|
mvs = sum([np.exp(k) for k in x.flatten()])
|
||||||
|
return np.array([np.exp(k)/mvs for k in x.flatten()])
|
||||||
|
|
||||||
|
|
||||||
|
def tanh(x, deriv=False):
|
||||||
|
if deriv:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return np.tanh(x)
|
||||||
|
|
||||||
|
|
||||||
|
def relu(x, deriv=False):
|
||||||
|
if deriv:
|
||||||
|
return 1. * (x > 0)
|
||||||
|
else:
|
||||||
|
return x * (x > 0)
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ parameters = {}
|
|||||||
|
|
||||||
#
|
#
|
||||||
def genotype():
|
def genotype():
|
||||||
'''
|
"""
|
||||||
Create the individual genotype
|
Create the individual genotype
|
||||||
|
|
||||||
:param mf: membership function
|
:param mf: membership function
|
||||||
@ -32,30 +32,30 @@ def genotype():
|
|||||||
:param f1: accuracy fitness value
|
:param f1: accuracy fitness value
|
||||||
:param f2: parsimony fitness value
|
:param f2: parsimony fitness value
|
||||||
:return: the genotype, a dictionary with all hyperparameters
|
:return: the genotype, a dictionary with all hyperparameters
|
||||||
'''
|
"""
|
||||||
num_concepts = parameters['num_concepts']
|
num_concepts = parameters['num_concepts']
|
||||||
order = parameters['order']
|
order = parameters['order']
|
||||||
ind = dict(weights=[np.random.normal(0,.5,(num_concepts,num_concepts)) for k in range(order)])
|
ind = dict(weights=[np.random.normal(0,1.,(num_concepts,num_concepts)) for k in range(order)])
|
||||||
return ind
|
return ind
|
||||||
|
|
||||||
|
|
||||||
def random_genotype():
|
def random_genotype():
|
||||||
'''
|
"""
|
||||||
Create random genotype
|
Create random genotype
|
||||||
|
|
||||||
:return: the genotype, a dictionary with all hyperparameters
|
:return: the genotype, a dictionary with all hyperparameters
|
||||||
'''
|
"""
|
||||||
return genotype()
|
return genotype()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
def initial_population(n):
|
def initial_population(n):
|
||||||
'''
|
"""
|
||||||
Create a random population of size n
|
Create a random population of size n
|
||||||
|
|
||||||
:param n: the size of the population
|
:param n: the size of the population
|
||||||
:return: a list with n random individuals
|
:return: a list with n random individuals
|
||||||
'''
|
"""
|
||||||
pop = []
|
pop = []
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
pop.append(random_genotype())
|
pop.append(random_genotype())
|
||||||
@ -63,14 +63,14 @@ def initial_population(n):
|
|||||||
|
|
||||||
|
|
||||||
def phenotype(individual, train):
|
def phenotype(individual, train):
|
||||||
'''
|
"""
|
||||||
Instantiate the genotype, creating a fitted model with the genotype hyperparameters
|
Instantiate the genotype, creating a fitted model with the genotype hyperparameters
|
||||||
|
|
||||||
:param individual: a genotype
|
:param individual: a genotype
|
||||||
:param train: the training dataset
|
:param train: the training dataset
|
||||||
:param parameters: dict with model specific arguments for fit method.
|
:param parameters: dict with model specific arguments for fit method.
|
||||||
:return: a fitted FTS model
|
:return: a fitted FTS model
|
||||||
'''
|
"""
|
||||||
partitioner = parameters['partitioner']
|
partitioner = parameters['partitioner']
|
||||||
order = parameters['order']
|
order = parameters['order']
|
||||||
|
|
||||||
@ -81,9 +81,8 @@ def phenotype(individual, train):
|
|||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate(dataset, individual, **kwargs):
|
def evaluate(dataset, individual, **kwargs):
|
||||||
'''
|
"""
|
||||||
Evaluate an individual using a sliding window cross validation over the dataset.
|
Evaluate an individual using a sliding window cross validation over the dataset.
|
||||||
|
|
||||||
:param dataset: Evaluation dataset
|
:param dataset: Evaluation dataset
|
||||||
@ -93,7 +92,7 @@ def evaluate(dataset, individual, **kwargs):
|
|||||||
:param increment_rate: The increment of the scrolling window, relative to the window_size ([0,1])
|
:param increment_rate: The increment of the scrolling window, relative to the window_size ([0,1])
|
||||||
:param parameters: dict with model specific arguments for fit method.
|
:param parameters: dict with model specific arguments for fit method.
|
||||||
:return: a tuple (len_lags, rmse) with the parsimony fitness value and the accuracy fitness value
|
:return: a tuple (len_lags, rmse) with the parsimony fitness value and the accuracy fitness value
|
||||||
'''
|
"""
|
||||||
from pyFTS.common import Util
|
from pyFTS.common import Util
|
||||||
from pyFTS.benchmarks import Measures
|
from pyFTS.benchmarks import Measures
|
||||||
from pyFTS.fcm.GA import phenotype
|
from pyFTS.fcm.GA import phenotype
|
||||||
@ -129,15 +128,14 @@ def evaluate(dataset, individual, **kwargs):
|
|||||||
return {'rmse': .6 * _rmse + .4 * _std}
|
return {'rmse': .6 * _rmse + .4 * _std}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def tournament(population, objective):
|
def tournament(population, objective):
|
||||||
'''
|
"""
|
||||||
Simple tournament selection strategy.
|
Simple tournament selection strategy.
|
||||||
|
|
||||||
:param population: the population
|
:param population: the population
|
||||||
:param objective: the objective to be considered on tournament
|
:param objective: the objective to be considered on tournament
|
||||||
:return:
|
:return:
|
||||||
'''
|
"""
|
||||||
n = len(population) - 1
|
n = len(population) - 1
|
||||||
|
|
||||||
r1 = random.randint(0, n) if n > 2 else 0
|
r1 = random.randint(0, n) if n > 2 else 0
|
||||||
@ -146,14 +144,13 @@ def tournament(population, objective):
|
|||||||
return population[ix]
|
return population[ix]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def crossover(parents):
|
def crossover(parents):
|
||||||
'''
|
"""
|
||||||
Crossover operation between two parents
|
Crossover operation between two parents
|
||||||
|
|
||||||
:param parents: a list with two genotypes
|
:param parents: a list with two genotypes
|
||||||
:return: a genotype
|
:return: a genotype
|
||||||
'''
|
"""
|
||||||
import random
|
import random
|
||||||
|
|
||||||
descendent = genotype()
|
descendent = genotype()
|
||||||
@ -164,7 +161,7 @@ def crossover(parents):
|
|||||||
weights2 = parents[1]['weights'][k]
|
weights2 = parents[1]['weights'][k]
|
||||||
|
|
||||||
for (row, col), a in np.ndenumerate(weights1):
|
for (row, col), a in np.ndenumerate(weights1):
|
||||||
new_weight.append(.7*weights1[row, col] + .3*weights2[row, col] )
|
new_weight.append(.7*weights1[row, col] + .3*weights2[row, col] )
|
||||||
|
|
||||||
descendent['weights'][k] = np.array(new_weight).reshape(weights1.shape)
|
descendent['weights'][k] = np.array(new_weight).reshape(weights1.shape)
|
||||||
|
|
||||||
@ -172,12 +169,12 @@ def crossover(parents):
|
|||||||
|
|
||||||
|
|
||||||
def mutation(individual, pmut):
|
def mutation(individual, pmut):
|
||||||
'''
|
"""
|
||||||
Mutation operator
|
Mutation operator
|
||||||
|
|
||||||
:param population:
|
:param population:
|
||||||
:return:
|
:return:
|
||||||
'''
|
"""
|
||||||
import numpy.random
|
import numpy.random
|
||||||
|
|
||||||
for k in range(parameters['order']):
|
for k in range(parameters['order']):
|
||||||
@ -197,18 +194,17 @@ def mutation(individual, pmut):
|
|||||||
individual['weights'][k][row, col] += np.random.normal(0, .5, 1)
|
individual['weights'][k][row, col] += np.random.normal(0, .5, 1)
|
||||||
individual['weights'][k][row, col] = np.clip(individual['weights'][k][row, col], -1, 1)
|
individual['weights'][k][row, col] = np.clip(individual['weights'][k][row, col], -1, 1)
|
||||||
|
|
||||||
|
|
||||||
return individual
|
return individual
|
||||||
|
|
||||||
|
|
||||||
def elitism(population, new_population):
|
def elitism(population, new_population):
|
||||||
'''
|
"""
|
||||||
Elitism operation, always select the best individual of the population and discard the worst
|
Elitism operation, always select the best individual of the population and discard the worst
|
||||||
|
|
||||||
:param population:
|
:param population:
|
||||||
:param new_population:
|
:param new_population:
|
||||||
:return:
|
:return:
|
||||||
'''
|
"""
|
||||||
population = sorted(population, key=itemgetter('rmse'))
|
population = sorted(population, key=itemgetter('rmse'))
|
||||||
best = population[0]
|
best = population[0]
|
||||||
|
|
||||||
@ -220,7 +216,7 @@ def elitism(population, new_population):
|
|||||||
|
|
||||||
|
|
||||||
def GeneticAlgorithm(dataset, **kwargs):
|
def GeneticAlgorithm(dataset, **kwargs):
|
||||||
'''
|
"""
|
||||||
Genetic algoritm for hyperparameter optimization
|
Genetic algoritm for hyperparameter optimization
|
||||||
|
|
||||||
:param dataset:
|
:param dataset:
|
||||||
@ -234,7 +230,7 @@ def GeneticAlgorithm(dataset, **kwargs):
|
|||||||
:param increment_rate: The increment of the scrolling window, relative to the window_size ([0,1])
|
:param increment_rate: The increment of the scrolling window, relative to the window_size ([0,1])
|
||||||
:param parameters: dict with model specific arguments for fit method.
|
:param parameters: dict with model specific arguments for fit method.
|
||||||
:return: the best genotype
|
:return: the best genotype
|
||||||
'''
|
"""
|
||||||
|
|
||||||
statistics = []
|
statistics = []
|
||||||
|
|
||||||
|
30
pyFTS/fcm/GD.py
Normal file
30
pyFTS/fcm/GD.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def GD(data, model, alpha, momentum=0.5):
|
||||||
|
num_concepts = model.partitioner.partitions
|
||||||
|
weights=[np.random.normal(0,.01,(num_concepts,num_concepts)) for k in range(model.order)]
|
||||||
|
last_gradient = [None for k in range(model.order) ]
|
||||||
|
for i in np.arange(model.order, len(data)):
|
||||||
|
sample = data[i-model.order : i]
|
||||||
|
target = data[i]
|
||||||
|
model.fcm.weights = weights
|
||||||
|
inputs = model.partitioner.fuzzyfy(sample, mode='vector')
|
||||||
|
activations = [model.fcm.activation_function(inputs[k]) for k in np.arange(model.order)]
|
||||||
|
forecast = model.predict(sample)[0]
|
||||||
|
error = target - forecast #)**2
|
||||||
|
if error == np.nan:
|
||||||
|
pass
|
||||||
|
print(error)
|
||||||
|
for k in np.arange(model.order):
|
||||||
|
deriv = error * model.fcm.activation_function(activations[k], deriv=True)
|
||||||
|
if momentum is not None:
|
||||||
|
if last_gradient[k] is None:
|
||||||
|
last_gradient[k] = deriv*inputs[k]
|
||||||
|
|
||||||
|
tmp_grad = (momentum * last_gradient[k]) + alpha*deriv*inputs[k]
|
||||||
|
weights[k] -= tmp_grad
|
||||||
|
else:
|
||||||
|
weights[k] -= alpha*deriv*inputs[k]
|
||||||
|
|
||||||
|
return weights
|
@ -8,7 +8,7 @@ class FuzzyCognitiveMap(object):
|
|||||||
self.order = kwargs.get('order',1)
|
self.order = kwargs.get('order',1)
|
||||||
self.concepts = kwargs.get('partitioner',None)
|
self.concepts = kwargs.get('partitioner',None)
|
||||||
self.weights = []
|
self.weights = []
|
||||||
self.activation_function = kwargs.get('func', Activations.sigmoid)
|
self.activation_function = kwargs.get('activation_function', Activations.sigmoid)
|
||||||
|
|
||||||
def activate(self, concepts):
|
def activate(self, concepts):
|
||||||
dot_products = np.zeros(len(self.concepts))
|
dot_products = np.zeros(len(self.concepts))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from pyFTS.common import fts
|
from pyFTS.common import fts
|
||||||
from pyFTS.models import hofts
|
from pyFTS.models import hofts
|
||||||
from pyFTS.fcm import common, GA, Activations
|
from pyFTS.fcm import common, GA, Activations, GD
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
@ -11,11 +11,14 @@ class FCM_FTS(hofts.HighOrderFTS):
|
|||||||
self.fcm = common.FuzzyCognitiveMap(**kwargs)
|
self.fcm = common.FuzzyCognitiveMap(**kwargs)
|
||||||
|
|
||||||
def train(self, data, **kwargs):
|
def train(self, data, **kwargs):
|
||||||
|
'''
|
||||||
GA.parameters['num_concepts'] = self.partitioner.partitions
|
GA.parameters['num_concepts'] = self.partitioner.partitions
|
||||||
GA.parameters['order'] = self.order
|
GA.parameters['order'] = self.order
|
||||||
GA.parameters['partitioner'] = self.partitioner
|
GA.parameters['partitioner'] = self.partitioner
|
||||||
ret = GA.execute(data, **kwargs)
|
ret = GA.execute(data, **kwargs)
|
||||||
self.fcm.weights = ret['weights']
|
self.fcm.weights = ret['weights']
|
||||||
|
'''
|
||||||
|
self.fcm.weights = GD.GD(data, self, alpha=0.01)
|
||||||
|
|
||||||
def forecast(self, ndata, **kwargs):
|
def forecast(self, ndata, **kwargs):
|
||||||
ret = []
|
ret = []
|
||||||
|
44
pyFTS/tests/fcm_fts.py
Normal file
44
pyFTS/tests/fcm_fts.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from pyFTS.fcm import Activations
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import matplotlib as plt
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import seaborn as sns
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from pyFTS.fcm import fts as fcm_fts
|
||||||
|
from pyFTS.partitioners import Grid
|
||||||
|
from pyFTS.common import Util
|
||||||
|
|
||||||
|
df = pd.read_csv('https://query.data.world/s/56i2vkijbvxhtv5gagn7ggk3zw3ksi', sep=';')
|
||||||
|
|
||||||
|
data = df['glo_avg'].values[:]
|
||||||
|
|
||||||
|
|
||||||
|
train = data[:7000]
|
||||||
|
test = data[7000:7500]
|
||||||
|
|
||||||
|
fs = Grid.GridPartitioner(data=train, npart=7)
|
||||||
|
|
||||||
|
model = fcm_fts.FCM_FTS(partitioner=fs, order=2, activation_function = Activations.relu)
|
||||||
|
|
||||||
|
model.fit(train,
|
||||||
|
ngen=30, #number of generations
|
||||||
|
mgen=7, # stop after mgen generations without improvement
|
||||||
|
npop=10, # number of individuals on population
|
||||||
|
pcruz=.5, # crossover percentual of population
|
||||||
|
pmut=.3, # mutation percentual of population
|
||||||
|
window_size = 7000,
|
||||||
|
train_rate = .8,
|
||||||
|
increment_rate =.2,
|
||||||
|
experiments=1
|
||||||
|
)
|
||||||
|
|
||||||
|
Util.persist_obj(model, 'fcm_fts10c')
|
||||||
|
'''
|
||||||
|
model = Util.load_obj('fcm_fts05c')
|
||||||
|
'''
|
||||||
|
|
||||||
|
forecasts = model.predict(test)
|
||||||
|
|
||||||
|
print(model)
|
Loading…
Reference in New Issue
Block a user