feat: adds bug fixes and Vitterbi algorithm
This commit is contained in:
@@ -16,7 +16,7 @@ class HMM:
|
||||
# B
|
||||
emission_matrix: np.ndarray
|
||||
|
||||
def __init__(self, emission_matrix_file_name: str, numeric_text: np.ndarray):
|
||||
def __init__(self, emission_matrix_file_name: str|None, numeric_text: np.ndarray):
|
||||
"""
|
||||
/!\\ long
|
||||
|
||||
@@ -32,14 +32,22 @@ class HMM:
|
||||
self.initial_probabilities = np.zeros(26)
|
||||
self.initial_probabilities[::] = 1 / 26 # les probabilités initiales sont 1/26 pour les 26 lettres
|
||||
|
||||
def generate_emission_matrix(self, file_name) -> None:
|
||||
def generate_emission_matrix(self, file_name: str|None) -> None:
|
||||
"""
|
||||
Lis le fichier de la matrice d'émission et la retourne
|
||||
sous forme de dataframe pandas.
|
||||
Lis le fichier de la matrice d'émission et l'assigne à l'attribut de la classe qui y correspond.
|
||||
Si le nom de fichier n'ai pas donné, une matrice identité est utilisée à la place
|
||||
|
||||
La matrice est sous format numpy.
|
||||
:param file_name:
|
||||
:return:
|
||||
"""
|
||||
self.emission_matrix = pd.read_excel(file_name).iloc[:, 1:].to_numpy(dtype=float)
|
||||
if file_name is None:
|
||||
self.emission_matrix = np.zeros(shape=(26,26))
|
||||
|
||||
for i in range(26):
|
||||
self.emission_matrix[i, i] = 1
|
||||
else:
|
||||
self.emission_matrix = pd.read_excel(file_name).iloc[:, 1:].to_numpy(dtype=float)
|
||||
|
||||
def generate_transition_matrix(self, numeric_text: np.ndarray) -> None:
|
||||
"""
|
||||
@@ -107,7 +115,7 @@ class HMM:
|
||||
N = len(self.initial_probabilities)
|
||||
beta = np.ones(N)
|
||||
T = len(O)
|
||||
# On remonte le temps de T-2 à 0
|
||||
|
||||
for t in range(T - 2, -1, -1):
|
||||
new_beta = np.zeros(N)
|
||||
for i in range(N):
|
||||
@@ -117,3 +125,35 @@ class HMM:
|
||||
|
||||
# résultat somme de pi_i * b_i(o_1) * beta_1(i)
|
||||
return np.sum([self.initial_probabilities[i] * self.emission_matrix[i, O[0]] * beta[i] for i in range(N)]), beta
|
||||
|
||||
def viterbi(self, O: list[int]) -> list[int]:
|
||||
"""
|
||||
|
||||
Note: je suis partis de cette algo : https://en.wikipedia.org/wiki/Viterbi_algorithm
|
||||
Je le trouve plus simple à lire, même si moins concis que celui du sujet.
|
||||
J'ai adapté les noms pour correspondre le plus possible à ceux du TP.
|
||||
:param O:
|
||||
:return:
|
||||
"""
|
||||
N = len(self.initial_probabilities)
|
||||
T = len(O)
|
||||
|
||||
dzeta = np.zeros((T, N))
|
||||
psi = np.zeros((T, N), dtype=int)
|
||||
|
||||
dzeta[0] = self.initial_probabilities * self.emission_matrix[:, O[0]]
|
||||
|
||||
for t in range(1, T):
|
||||
for j in range(N):
|
||||
trans_probs = dzeta[t - 1] * self.transition_matrix[:, j]
|
||||
best_r = np.argmax(trans_probs)
|
||||
dzeta[t, j] = trans_probs[best_r] * self.emission_matrix[j, O[t]]
|
||||
psi[t, j] = best_r
|
||||
|
||||
best_path = np.zeros(T, dtype=int)
|
||||
best_path[T - 1] = np.argmax(dzeta[T - 1])
|
||||
|
||||
for t in range(T - 2, -1, -1):
|
||||
best_path[t] = psi[t + 1, best_path[t + 1]]
|
||||
|
||||
return best_path.tolist()
|
||||
|
||||
Reference in New Issue
Block a user