#!/usr/bin/env python3 import numpy as np if not __file__.endswith('_hmm_aspect.py'): print('ERROR: This file is not named correctly! Please name it as Lastname_hmm_aspect.py (replacing Lastname with your last name)!') exit(1) DATA_PATH = "/u/cs446/data/em/" #TODO: if doing development somewhere other than the cycle server (not recommended), then change this to the directory where your data file is (points.dat) def parse_data(args): num = int dtype = np.int data = [] with open(args.data_file, 'r') as f: for line in f: data.append([num(t) for t in line.split()]) dev_cutoff = int(.9*len(data)) train_xs = np.asarray(data[:dev_cutoff],dtype=dtype) dev_xs = np.asarray(data[dev_cutoff:],dtype=dtype) if not args.nodev else None return train_xs, dev_xs def init_model(args): if args.cluster_num: theta_1 = np.zeros((args.cluster_num,10)) #theta_1[k][i] = p(x_1 = i | z = k); this was called alphas before, but has been renamed because alpha is the HMM forward variable theta_2 = np.zeros((args.cluster_num,10)) #theta_2[k][i] = p(x_2 = i | z = k); also renamed transitions = np.zeros((args.cluster_num,args.cluster_num)) #transitions[i][j] = probability of moving from cluster i to cluster j initials = np.zeros(args.cluster_num) #probability for starting in each state #TODO: randomly initialize clusters (theta_1, theta_2, initials, and transitions) raise NotImplementedError #remove when random initialization is implemented else: theta_1 = [] theta_2 = [] transitions = [] initials = [] with open(args.clusters_file,'r') as f: for line in f: #each line is a cluster, and looks like this: #initial a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 transition_this_to_0 transition_this_to_1 ... transition_this_to_K-1 vals = list(map(float,line.split())) initials.append(vals[0]) theta_1.append(vals[1:11]) theta_2.append(vals[11:21]) transitions.append(vals[21:]) initials = np.asarray(initials) transitions = np.asarray(transitions) theta_1 = np.asarray(theta_1) theta_2 = np.asarray(theta_2) args.cluster_num = len(initials) #TODO: Do whatever you want to pack theta_1, theta_2, initials, and transitions into the model variable (just a tuple, or a class, etc.) model = None raise NotImplementedError #remove when model initialization is implemented return model def forward(model, data, args): from math import log alphas = np.zeros((len(data),args.cluster_num)) log_likelihood = 0.0 #TODO: Calculate and return forward probabilities (normalized at each timestep; see next line) and log_likelihood #NOTE: To avoid numerical problems, calculate the sum of alpha[t] at each step, normalize alpha[t] by that value, and increment log_likelihood by the log of the value you normalized by. This will prevent the probabilities from going to 0, and the scaling will be cancelled out in train_model when you normalize (you don't need to do anything different than what's in the notes). This was discussed in class on April 3rd. raise NotImplementedError #remove when forward and log_likelihood calculations are implemented return alphas, log_likelihood def backward(model, data, args): betas = np.zeros((len(data),args.cluster_num)) #TODO: Calculate and return backward probabilities (normalized like in forward before) raise NotImplementedError #remove when backward calculation is implemented return betas def train_model(model, train_xs, dev_xs, args): #TODO: train the model, respecting args (note that dev_xs is None if args.nodev is True) raise NotImplementedError #remove when model training is implemented return model def average_log_likelihood(model, data, args): #TODO: implement average LL calculation (log likelihood of the data, divided by the length of the data) #NOTE: yes, this is very simple, because you did most of the work in the forward function above ll = 0.0 raise NotImplementedError #remove when average log likelihood calculation is implemented return ll def extract_parameters(model): #TODO: Extract initials, transitions, theta_1, and theta_2 from the model and return them (same type and shape as in init_model) initials = None transitions = None theta_1 = None theta_2 = None raise NotImplementedError #remove when parameter extraction is implemented return initials, transitions, theta_1, theta_2 def main(): import argparse import os print('Aspect') #Do not change, and do not print anything before this. parser = argparse.ArgumentParser(description='Use EM to fit a set of points') init_group = parser.add_mutually_exclusive_group(required=True) init_group.add_argument('--cluster_num', type=int, help='Randomly initialize this many clusters.') init_group.add_argument('--clusters_file', type=str, help='Initialize clusters from this file.') parser.add_argument('--nodev', action='store_true', help='If provided, no dev data will be used.') parser.add_argument('--data_file', type=str, default=os.path.join(DATA_PATH, 'pairs.dat'), help='Data file.') parser.add_argument('--print_params', action='store_true', help='If provided, learned parameters will also be printed.') parser.add_argument('--iterations', type=int, default=1, help='Number of EM iterations to perform') args = parser.parse_args() train_xs, dev_xs = parse_data(args) model = init_model(args) model = train_model(model, train_xs, dev_xs, args) nll_train = average_log_likelihood(model, train_xs, args) print('Train LL: {}'.format(nll_train)) if not args.nodev: nll_dev = average_log_likelihood(model, dev_xs, args) print('Dev LL: {}'.format(nll_dev)) initials, transitions, theta_1, theta_2 = extract_parameters(model) if args.print_params: def intersperse(s): return lambda a: s.join(map(str,a)) print('Initials: {}'.format(intersperse(' | ')(np.nditer(initials)))) print('Transitions: {}'.format(intersperse(' | ')(map(intersperse(' '),transitions)))) print('Theta_1: {}'.format(intersperse(' | ')(map(intersperse(' '),theta_1)))) print('Theta_2: {}'.format(intersperse(' | ')(map(intersperse(' '),theta_2)))) if __name__ == '__main__': main()