예제 #1
0
def calc_gamma(alpha, beta, gamma):
    """
    根据alphe和beta的值计算gamma值
    最终结果保存在gamma矩阵中
    :param alpha: 传入的alpha矩阵 => 经过log转换后的
    :param beta:  传入的beta矩阵 => 经过log转换后的
    :param gamma: 传入的gamma矩阵,需要进行更新,最终结果是经过log转换后的
    :return: gamma矩阵
    """
    # 1. 获取相关变量
    T, n = np.shape(alpha)

    # 2. 遍历更新
    for t in range(T):
        # 2.1. 累加alpha和beta值(ppt上分子部分)
        for i in range(n):
            gamma[t][i] = alpha[t][i] + beta[t][i]

        # 2.2. 计算log_sum_exp的值(ppt上分母部分)
        lse = log_sum_exp(gamma[t])

        # 2.3. 计算最终结果
        for i in range(n):
            gamma[t][i] -= lse

    # 3. 返回最终结果
    return gamma
예제 #2
0
def calc_alpha(pi, A, B, Q, alpha, fetch_index_by_obs_seq=None):
    """
    计算前向概率α的值
    :param pi: 初始的状态随机概率矩阵, n*1 => 经过对数转换
    :param A:  状态转移矩阵, n*n => 经过对数转换
    :param B:  状态和观测值之间的转移矩阵, n*m => 经过对数转换
    :param Q:  观测值序列, 长度为T
    :param alpha:  前向概率矩阵
    :param fetch_index_by_obs_seq: 根据序列获取对应的索引值,可以为None;是一个接受两个参数的函数,第一个参数为序列,第二个参数为索引值
    :return:  返回alpha矩阵同时也更新传入的alpha矩阵参数
    """
    # 1. 初始化
    fetch_index_by_obs_seq_f = fetch_index_by_obs_seq
    if fetch_index_by_obs_seq_f is None:
        # 默认是使用ord函数将对应位置的字符转换为ASCII码,eg: ord('a')=97; ord('中')=20013
        fetch_index_by_obs_seq_f = lambda obs, obs_index: ord(obs[obs_index])

    # 2. 获取相关变量
    n = np.shape(A)[0]
    T = len(Q)

    # 3. 更新t=1时刻(初始时刻)对应的前向概率值
    for i in range(n):
        alpha[0][i] = pi[i] + B[i][fetch_index_by_obs_seq_f(Q, 0)]

    # 4. 更新t=2,3....T时刻对应的前向概率值
    tmp = [0 for i in range(n)]
    for t in range(1, T):
        # TODO: 开始更新时刻t、状态为i的前向概率值
        for i in range(n):
            # 4.1. 计算累加值
            for j in range(n):
                tmp[j] = alpha[t - 1][j] + A[j][i]

            # 4.2. 计算log_sum_exp的值
            alpha[t][i] = log_sum_exp(tmp)

            # 4.3. 累加状态和观测值之间的转移矩阵
            alpha[t][i] += B[i][fetch_index_by_obs_seq_f(Q, t)]

    # 5. 返回最终返回值
    return alpha
예제 #3
0
def calc_beta(pi, A, B, Q, beta, fetch_index_by_obs_seq=None):
    """
    计算后向概率β的值
    :param pi: 初始的状态随机概率矩阵, n*1 => 经过对数转换
    :param A:  状态转移矩阵, n*n => 经过对数转换
    :param B:  状态和观测值之间的转移矩阵, n*m => 经过对数转换
    :param Q:  观测值序列, 长度为T
    :param beta: 后向概率矩阵
    :param fetch_index_by_obs_seq: 根据序列获取对应的索引值,可以为None;是一个接受两个参数的函数,第一个参数为序列,第二个参数为索引值
    :return:  返回beta矩阵同时也更新传入的beta矩阵参数
    """
    # 1. 初始化
    fetch_index_by_obs_seq_f = fetch_index_by_obs_seq
    if fetch_index_by_obs_seq_f is None:
        # 默认是使用ord函数将对应位置的字符转换为ASCII码,eg: ord('a')=97; ord('中')=20013
        fetch_index_by_obs_seq_f = lambda obs, obs_index: ord(obs[obs_index])

    # 2. 获取相关变量
    n = np.shape(A)[0]
    T = len(Q)

    # 3. 更新t=T时刻(初始时刻)对应的后向概率值
    for i in range(n):
        beta[T - 1][i] = 0

    # 4. 更新t=T-1,T-2....1时刻对应的前向概率值
    tmp = [0 for i in range(n)]
    for t in range(T - 2, -1, -1):
        # TODO: 更新时刻t对应状态为i的后向概率
        for i in range(n):
            # 4.1 计算累加值
            for j in range(n):
                tmp[j] = A[i][j] + beta[t +
                                        1][j] + B[j][fetch_index_by_obs_seq_f(
                                            Q, t + 1)]

            # 4.2 计算log_sum_exp的值
            beta[t][i] = log_sum_exp(tmp)

    # 5. 结果返回
    return beta
예제 #4
0
def calc_ksi(alpha, beta, A, B, Q, ksi, fetch_index_by_obs_seq=None):
    """
    计算时刻t的时候状态为i,时刻t+1的时候状态为j的联合概率ksi
    :param alpha:  传入的alpha矩阵 => 经过log转换后的
    :param beta:  传入的beta矩阵 => 经过log转换后的
    :param A: 状态转移矩阵, n*n => 经过对数转换
    :param B:  状态和观测值之间的转移矩阵, n*m => 经过对数转换
    :param Q:  观测值序列, 长度为T
    :param ksi: 待求解的ksi概率矩阵,最终结果是需要经过log转换的
    :param fetch_index_by_obs_seq: 根据序列获取对应的索引值,可以为None;是一个接受两个参数的函数,第一个参数为序列,第二个参数为索引值
    :return: 返回ksi矩阵
    """
    # 1. 初始化
    # 初始化序列转换为索引的方法
    fetch_index_by_obs_seq_f = fetch_index_by_obs_seq
    if fetch_index_by_obs_seq_f is None:
        fetch_index_by_obs_seq_f = lambda obs, obs_index: ord(obs[obs_index])

    # 2. 变量获取
    T, n = np.shape(alpha)

    # 3. 开始迭代更新ksi矩阵
    tmp = np.zeros((n, n))
    for t in range(T - 1):
        # 3.1 计算t时刻状态为i,t+1时刻状态为j的概率值(ppt上的分子部分)
        for i in range(n):
            for j in range(n):
                tmp[i][j] = alpha[t][i] + A[i][j] + beta[t + 1][j] + B[j][
                    fetch_index_by_obs_seq_f(Q, t + 1)]

        # 3.2 计算log_sum_exp的值(ppt上分母部分)
        lse = log_sum_exp(tmp.flat)

        # 3.3 计算最终的结果值
        for i in range(n):
            for j in range(n):
                ksi[t][i][j] = tmp[i][j] - lse

    # 4. 返回最终结果
    ksi
예제 #5
0
def baum_welch(pi, A, B, Q, max_iter=10, fetch_index_by_obs_seq=None):
    """
    根据传入的初始概率矩阵(pi、A、B)以及观测序列Q,使用baum_welch算法进行迭代求解最终的pi、A、B的值;最大迭代次数默认为10;最终更新结果保存在传入的参数矩阵中(pi\A\B)
    :param pi: 初始的状态随机概率矩阵, n*1 => 经过对数转换
    :param A:  状态转移矩阵, n*n => 经过对数转换
    :param B:  状态和观测值之间的转移矩阵, n*m => 经过对数转换
    :param Q:  观测值序列, 长度为T
    :param max_iter: 最大迭代次数,默认为10
    :param fetch_index_by_obs_seq: 根据序列获取对应的索引值,可以为None;是一个接受两个参数的函数,第一个参数为序列,第二个参数为索引值
    :return:  返回(pi, A, B)
    """
    # 1. 初始化
    fetch_index_by_obs_seq_f = fetch_index_by_obs_seq
    if fetch_index_by_obs_seq_f is None:
        # 默认是使用ord函数将对应位置的字符转换为ASCII码,eg: ord('a')=97; ord('中')=20013
        fetch_index_by_obs_seq_f = lambda obs, obs_index: ord(obs[obs_index])

    # 2. 初始化相关参数
    T = len(Q)
    n = np.shape(A)[0]
    if len(np.shape(B)) == 1:
        m = 1
    else:
        m = np.shape(B)[1]
    alpha_ = np.zeros((T, n))
    beta_ = np.zeros((T, n))
    gamma_ = np.zeros((T, n))
    ksi_ = np.zeros((T - 1, n, n))

    # 3. 开始遍历求解
    for time in range(max_iter):
        # 3.1 分别计算在当前情况下的alphe、beta、gamma、ksi的值
        calc_alpha(pi, A, B, Q, alpha_, fetch_index_by_obs_seq_f)
        calc_beta(pi, A, B, Q, beta_, fetch_index_by_obs_seq_f)
        calc_gamma(alpha_, beta_, gamma_)
        calc_ksi(alpha_, beta_, A, B, Q, ksi_, fetch_index_by_obs_seq_f)

        # 3.2 更新pi的值
        for i in range(n):
            pi[i] = gamma_[0][i]

        # 3.3 更新A的值
        tmp1 = np.zeros(T - 1)  # 对应ppt上分子
        tmp2 = np.zeros(T - 1)  # 对应ppt上的分母
        for i in range(n):
            for j in range(n):
                # 3.3.1 获取所有时刻从状态i转移到状态j的概率值
                for t in range(T - 1):
                    tmp1[t] = ksi_[t][i][j]
                    tmp2[t] = gamma_[t][i]

                # 3.3.2 计算状态转移矩阵
                A[i][j] = log_sum_exp(tmp1) - log_sum_exp(tmp2)

        # 3.4 更新B的值
        tmp1 = np.zeros(T)  # 对应ppt上分子
        tmp2 = np.zeros(T)  # 对应ppt上的分母
        for i in range(n):
            for o in range(m):
                # 3.4.1 获取所有时刻位于状态i的概率
                valid = 0
                for t in range(T):
                    # a. 计算分子的值
                    if o == fetch_index_by_obs_seq_f(Q, t):
                        # 当前观测值和对应观测值一致,计算分子
                        tmp1[valid] = gamma_[t][i]
                        valid += 1
                    # b. 累积分母的值
                    tmp2[t] = gamma_[t][i]

                # 3.4.2 更新状态转移矩阵的值B
                if valid == 0:
                    # 表示没有从i和o的观测值转移矩阵
                    B[i][o] = 0
                else:
                    B[i][o] = log_sum_exp(tmp1[:valid]) - log_sum_exp(tmp2)

    # 4. 返回最终结果
    return (pi, A, B)
예제 #6
0
    B = np.log(B)

    Q = ['白', '黑', '白', '白', '黑']
    T = len(Q)
    n = len(A)

    print("测试前向概率计算....................")
    alpha = np.zeros((T, n))
    # 开始计算
    hmm_learn.calc_alpha(pi, A, B, Q, alpha, common.convert_obs_seq_2_index)
    # 输出最终结果
    # print(np.exp(alpha))
    print(alpha)

    # 计算最终概率值:
    p = common.log_sum_exp(alpha[T - 1].flat)
    print(Q, end="->出现的概率为:")
    print(np.exp(p))

    print("测试后向概率计算....................")
    beta = np.zeros((T, n))
    # 开始计算
    hmm_learn.calc_beta(pi, A, B, Q, beta, common.convert_obs_seq_2_index)
    # 输出最终结果
    # print(np.exp(beta))
    print(beta)

    # 计算最终概率值:
    tmp_p = np.zeros(n)
    for i in range(n):
        tmp_p[i] = pi[i] + B[i][common.convert_obs_seq_2_index(Q, 0)] + beta[0][i]