Source code for abstractnn.relaxer

"""
Relaxation des fonctions d'activation non-linéaires
Nota : les relaxations proposées dans ce projets sont grossières (nouvelle approximation affines des bornes inf et sup du domaine.
Avantage, la propagation dans le réseau se fait à iso périmètre de symboles
Inconvénient : bornes du domaine plus larges qu'une méthode par intérpolation (par exemple Chebydchev)
"""

import numpy as np
from typing import Tuple
from .affine_engine import AffineExpression


[docs] class NonLinearRelaxer: """Gère les relaxations convexes des activations non-linéaires Cette relaxation est grossière car elle fait perdre la relation afine avec l'entrée mais reste très simple""" #TODO : gérer le cas ou epsilon = 0 pour éviter division par 0
[docs] @staticmethod def relu_relaxation(expr: AffineExpression, relaxation_type: str = 'linear') -> AffineExpression: """ Relaxation de ReLU: z = max(0, y) Si y ∈ [l, u]: - Si u ≤ 0 → z = 0 - Si l ≥ 0 → z = y - Sinon → relaxation linéaire: z ≤ (u/(u-l)) * (y - l) """ l, u = expr.get_bounds() # Cas où l'activation est toujours désactivée if u <= 0: return AffineExpression(0.0, {}, expr.bounds) # Cas où l'activation est toujours active if l >= 0: return expr # Cas ambigu: relaxation linéaire if relaxation_type == 'linear': # z ≥ 0, z ≥ y, z ≤ (u/(u-l)) * (y - l) slope = u / (u - l) new_coeffs = {idx: coeff * slope for idx, coeff in expr.coefficients.items()} new_constant = (expr.constant - l) * slope return AffineExpression(new_constant, new_coeffs, expr.bounds) return expr
[docs] @staticmethod def sigmoid_relaxation(expr: AffineExpression) -> AffineExpression: """Relaxation de Sigmoid (approximation linéaire)""" l, u = expr.get_bounds() # Approximation linéaire au point milieu mid = (l + u) / 2 sig_mid = 1 / (1 + np.exp(-mid)) slope = sig_mid * (1 - sig_mid) new_coeffs = {idx: coeff * slope for idx, coeff in expr.coefficients.items()} new_constant = sig_mid - slope * mid + expr.constant * slope return AffineExpression(new_constant, new_coeffs, expr.bounds)
[docs] @staticmethod def tanh_relaxation(expr: AffineExpression) -> AffineExpression: """Relaxation de Tanh (approximation linéaire)""" l, u = expr.get_bounds() # Approximation linéaire mid = (l + u) / 2 tanh_mid = np.tanh(mid) slope = 1 - tanh_mid ** 2 new_coeffs = {idx: coeff * slope for idx, coeff in expr.coefficients.items()} new_constant = tanh_mid - slope * mid + expr.constant * slope return AffineExpression(new_constant, new_coeffs, expr.bounds)