# coding: utf-8
"""Loss function to optimize 0th and 1st-order property."""
import warnings
import chainer
import chainer.functions as F
from hdnnpy.training.loss_function.loss_functions_base import (
LossFunctionBase)
[docs]class First(LossFunctionBase):
"""Loss function to optimize 0th and 1st-order property."""
name = 'first'
"""str: Name of this loss function class."""
order = {
'descriptor': 1,
'property': 1,
}
"""dict: Required orders of each dataset to calculate loss function.
"""
def __init__(self, model, properties, mixing_beta, **_):
"""
Args:
model (HighDimensionalNNP):
HDNNP object to optimize parameters.
properties (list [str]): Names of properties to optimize.
mixing_beta (float):
Mixing parameter of errors of 0th and 1st order.
It accepts 0.0 to 1.0. If 0.0 it optimizes HDNNP by only
0th order property and it is equal to loss function
``Zeroth``. If 1.0 it optimizes HDNNP by only 1st order
property.
"""
assert 0.0 <= mixing_beta <= 1.0
super().__init__(model)
self._observation_keys = [
f'RMSE/{properties[0]}', f'RMSE/{properties[1]}', 'total']
self._mixing_beta = mixing_beta
if mixing_beta == 0.0:
warnings.warn(
'If mixing_beta=0.0, you should use loss function type '
'`zeroth` instead of `first`.')
[docs] def eval(self, **dataset):
"""Calculate loss function from given datasets and model.
Args:
**dataset (~numpy.ndarray):
Datasets passed as kwargs. Name of each key is in the
format 'inputs/N' or 'labels/N'. 'N' is the order of
the dataset.
Returns:
~chainer.Variable:
A scalar value calculated with loss function.
"""
inputs = [dataset[f'inputs/{i}'] for i
in range(self.order['descriptor'] + 1)]
labels = [dataset[f'labels/{i}'] for i
in range(self.order['property'] + 1)]
predictions = self._model.predict(inputs, self.order['descriptor'])
loss0 = F.mean_squared_error(predictions[0], labels[0])
loss1 = F.mean_squared_error(predictions[1], labels[1])
total_loss = ((1.0 - self._mixing_beta) * loss0
+ self._mixing_beta * loss1)
RMSE0 = F.sqrt(loss0)
RMSE1 = F.sqrt(loss1)
total = ((1.0 - self._mixing_beta) * RMSE0
+ self._mixing_beta * RMSE1)
observation = {
self._observation_keys[0]: RMSE0,
self._observation_keys[1]: RMSE1,
self._observation_keys[2]: total,
}
chainer.report(observation, observer=self._model)
return total_loss