Пример #1
0
class Client():
    def __init__(self):

        self.Helpers = Helpers()

        self.Helpers = Helpers()
        self.confs = self.Helpers.loadConfigs()
        self.LogFile = self.Helpers.setLogFile(self.confs["aiCore"]["Logs"] +
                                               "Client/")

        self.apiUrl = "http://" + self.confs["aiCore"]["IP"] + ":" + str(
            self.confs["aiCore"]["Port"]) + "/infer"
        self.headers = {"content-type": 'application/json'}

        self.Helpers.logMessage(self.LogFile, "CLIENT", "INFO", "Client Ready")
Пример #2
0
class Data():
    def __init__(self):

        ###############################################################
        #
        # Sets up all default requirements and placeholders
        # needed for the NLU engine to run.
        #
        # - Helpers: Useful global functions
        # - Logging: Logging class
        # - LancasterStemmer: Word stemmer
        #
        ###############################################################

        self.ignore = [',', '.', '!', '?']

        self.Helpers = Helpers()
        self.confs = self.Helpers.loadConfigs()
        self.LogFile = self.Helpers.setLogFile(self.confs["aiCore"]["Logs"] +
                                               "JumpWay/")

        self.LancasterStemmer = LancasterStemmer()

    def loadTrainingData(self):

        ###############################################################
        #
        # Loads the NLU and NER training data from Model/Data/training.json
        #
        ###############################################################

        with open("Model/Data/training.json") as jsonData:
            trainingData = json.load(jsonData)

        self.Helpers.logMessage(self.LogFile, "Data", "INFO",
                                "Training Data Ready")

        return trainingData

    def loadTrainedData(self):

        ###############################################################
        #
        # Loads the saved training configuratuon
        #
        ###############################################################

        with open("Model/model.json") as jsonData:
            modelData = json.load(jsonData)

        self.Helpers.logMessage(self.LogFile, "Data", "INFO",
                                "Model Data Ready")

        return modelData

    def sortList(self, listToSort):

        ###############################################################
        #
        # Sorts a list by sorting the list, and removing duplicates
        #
        # https://www.programiz.com/python-programming/methods/built-in/sorted
        # https://www.programiz.com/python-programming/list
        # https://www.programiz.com/python-programming/set
        #
        ###############################################################

        return sorted(list(set(listToSort)))

    def extract(self, data=None, splitIt=False):

        ###############################################################
        #
        # Extracts words from sentences, stripping out characters in
        # the ignore list above
        #
        # https://www.nltk.org/_modules/nltk/stem/lancaster.html
        # http://insightsbot.com/blog/R8fu5/bag-of-words-algorithm-in-python-introduction
        #
        ###############################################################

        return [
            self.LancasterStemmer.stem(word)
            for word in (data.split() if splitIt == True else data)
            if word not in self.ignore
        ]

    def makeBagOfWords(self, sInput, words):

        ###############################################################
        #
        # Makes a bag of words used by the inference and training
        # features. If makeBagOfWords is called during training, sInput
        # will be a list.
        #
        # http://insightsbot.com/blog/R8fu5/bag-of-words-algorithm-in-python-introduction
        #
        ###############################################################

        if type(sInput) == list:
            bagOfWords = []
            for word in words:
                if word in sInput:
                    bagOfWords.append(1)
                else:
                    bagOfWords.append(0)
            return bagOfWords

        else:
            bagOfWords = np.zeros(len(words))
            for cword in self.extract(sInput, True):
                for i, word in enumerate(words):
                    if word == cword: bagOfWords[i] += 1
            return np.array(bagOfWords)

    def prepareClasses(self, intent, classes):

        ###############################################################
        #
        # Adds an intent key to classes if it does not already exist
        #
        ###############################################################

        if intent not in classes: classes.append(intent)
        return classes

    def prepareData(self,
                    trainingData=[],
                    wordsHldr=[],
                    dataCorpusHldr=[],
                    classesHldr=[]):

        ###############################################################
        #
        # Prepares the NLU and NER training data, loops through the
        # intents from our dataset, converts any entities / synoynms
        #
        ###############################################################

        counter = 0
        intentMap = {}

        for intent in trainingData['intents']:

            theIntent = intent['intent']
            for text in intent['text']:

                if 'entities' in intent and len(intent['entities']):
                    i = 0
                    for entity in intent['entities']:
                        tokens = text.replace(
                            trainingData['intents'][counter]["text"][i],
                            "<" + entity["entity"] + ">").lower().split()
                        wordsHldr.extend(tokens)
                        dataCorpusHldr.append((tokens, theIntent))
                        i = i + 1
                else:
                    tokens = text.lower().split()
                    wordsHldr.extend(tokens)
                    dataCorpusHldr.append((tokens, theIntent))

            intentMap[theIntent] = counter
            classesHldr = self.prepareClasses(theIntent, classesHldr)
            counter = counter + 1

        return self.sortList(self.extract(
            wordsHldr,
            False)), self.sortList(classesHldr), dataCorpusHldr, intentMap

    def finaliseData(self, classes, dataCorpus, words):

        ###############################################################
        #
        # Finalises the NLU training data
        #
        ###############################################################

        trainData = []
        out = np.zeros(len(classes))

        for document in dataCorpus:
            output = list(out)
            output[classes.index(document[1])] = 1
            trainData.append([
                self.makeBagOfWords(self.extract(document[0], False), words),
                output
            ])

        random.shuffle(trainData)
        trainData = np.array(trainData)

        self.Helpers.logMessage(self.LogFile, "Data", "INFO",
                                "Finalised Training Data Ready")

        return list(trainData[:, 0]), list(trainData[:, 1])
class Trainer():

    ###############################################################
    #
    # Sets up all default requirements and placeholders
    # needed for the NLU engine to run.
    #
    # - Helpers: Useful global functions
    # - JumpWay/jumpWayClient: iotJumpWay class and connection
    # - Logging: Logging class
    #
    ###############################################################

    def __init__(self):

        self.Helpers = Helpers()
        self._confs = self.Helpers.loadConfigs()
        self.LogFile = self.Helpers.setLogFile(self._confs["aiCore"]["Logs"] +
                                               "Train/")

        self.intentMap = {}
        self.words = []
        self.classes = []
        self.dataCorpus = []

        self.Model = Model()
        self.Data = Data()

    def setupData(self):

        self.trainingData = self.Data.loadTrainingData()

        self.words, self.classes, self.dataCorpus, self.intentMap = self.Data.prepareData(
            self.trainingData)
        self.x, self.y = self.Data.finaliseData(self.classes, self.dataCorpus,
                                                self.words)

        self.Helpers.logMessage(self.LogFile, "TRAIN", "INFO",
                                "NLU Training Data Ready")

    def setupEntities(self):

        if self._confs["NLU"]["Entities"] == "Mitie":
            self.entityController = Entities()
            self.entityController.trainEntities(
                self._confs["NLU"]["Mitie"]["ModelLocation"],
                self.trainingData)
            self.Helpers.logMessage(self.LogFile, "TRAIN", "OK",
                                    "NLU Trainer Entities Ready")

    def trainModel(self):

        while True:
            self.Helpers.logMessage(self.LogFile, "TRAIN", "ACTION",
                                    "Ready To Begin Training ? (Yes/No)")
            userInput = input(">")

            if userInput == 'Yes': break
            if userInput == 'No': exit()

        self.setupData()
        self.setupEntities()

        humanStart, trainingStart = self.Helpers.timerStart()

        self.Model.trainDNN(self.x, self.y, self.words, self.classes,
                            self.intentMap)

        trainingEnd, trainingTime, humanEnd = self.Helpers.timerEnd(
            trainingStart)

        self.Helpers.logMessage(
            self.LogFile, "TRAIN", "OK", "NLU Model Trained At " + humanEnd +
            " In " + str(trainingEnd) + " Seconds")
Пример #4
0
class NLU():
    	
	def __init__(self):

		###############################################################
		#
		# Sets up all default requirements and placeholders 
		# needed for the NLU engine to run. 
		#
		# - Helpers: Useful global functions
		# - Logging: Logging class
		#
		###############################################################
			
		self.isTraining    = False 
		self.ner           = None 
    		
		self.Helpers       = Helpers()
		self.confs        = self.Helpers.loadConfigs()

		self.user          = {}

		self.LogFile       = self.Helpers.setLogFile(self.confs["aiCore"]["Logs"]+"NLU/")
		self.ChatLogFile   = self.Helpers.setLogFile(self.confs["aiCore"]["Logs"]+"Chat/")
		
	def initiateSession(self):

		###############################################################
		#
		# Initiates empty guest user session, GeniSys will ask the user 
		# verify their GeniSys user by speaking or typing if it does 
		# not know who it is speaking to. 
		#
		###############################################################

		self.userID = 0
		if not self.userID in self.user:
			self.user[self.userID] = {}
			self.user[self.userID]["history"] = {}

	def initNLU(self):

		###############################################################
		#
		# Initiates the NLU setting up the data, NLU / entities models 
		# and required modules such as context and extensions.
		#
		###############################################################

		self.Data          = Data()
		self.trainingData  = self.Data.loadTrainingData()
		self.trainedData   = self.Data.loadTrainedData()
		
		self.Model         = Model()
		self.Context       = Context()
		self.Extensions    = Extensions()

		self.restoreData()
		self.restoreNER()
		self.restoreNLU()

		self.initiateSession()
		self.setThresholds()
		
	def commandsCallback(self,topic,payload):

		###############################################################
		#
		# The callback function that is triggerend in the event of a 
		# command communication from the iotJumpWay.
		#
		###############################################################
		
		self.Helpers.logMessage(
			self.LogFile,
			"iotJumpWay",
			"INFO",
			"Recieved iotJumpWay Command Data : " + str(payload))
			
		commandData = json.loads(payload.decode("utf-8"))

	def restoreData(self):

		###############################################################
		#
		# Sets the local trained data using data retrieved above
		#
		###############################################################

		self.trainedWords   = self.trainedData["words"]
		self.trainedClasses = self.trainedData["classes"]
		self.x              = self.trainedData["x"]
		self.y              = self.trainedData["y"]
		self.intentMap      = self.trainedData["intentMap"][0]
		
	def loadEntityController(self):

		###############################################################
		#
		# Initiates the entity extractor class from tools
		#
		###############################################################

		self.entityController = Entities()
		
	def restoreNER(self):

		###############################################################
		#
		# Loads entity controller and restores the NER model
		#
		###############################################################

		self.loadEntityController()
		self.ner = self.entityController.restoreNER()
		
	def restoreNLU(self):

		###############################################################
		#
		# Restores the NLU model
		#
		###############################################################

		self.tmodel = self.Model.buildDNN(self.x, self.y) 
		
	def setThresholds(self):

		###############################################################
		#
		# Sets the threshold for the NLU engine, this can be changed
		# using arguments to commandline programs or paramters for 
		# API calls.
		#
		###############################################################

		self.threshold     = self.confs["NLU"]["Threshold"]
		self.entityThrshld = self.confs["NLU"]["Mitie"]["Threshold"]
			
	def communicate(self, sentence):

		###############################################################
		#
		# First checks to ensure that the program is not training, 
		# then parses any entities that may be in the intent, then 
		# checks context and extensions before providing a response.
		#
		###############################################################

		if self.isTraining == False:

			parsed, fallback, entityHolder, parsedSentence = self.entityController.parseEntities(
				sentence,
				self.ner,
				self.trainingData
			)
			
			classification = self.Model.predict(self.tmodel, parsedSentence, self.trainedWords, self.trainedClasses)

			if len(classification) > 0:

				clearEntities = False
				theIntent     = self.trainingData["intents"][self.intentMap[classification[0][0]]]

				if len(entityHolder) and not len(theIntent["entities"]):
					clearEntities = True

				if(self.Context.checkSessionContext(self.user[self.userID], theIntent)):

					if self.Context.checkClearContext(theIntent, 0): 
						self.user[self.userID]["context"] = ""

					contextIn, contextOut, contextCurrent = self.Context.setContexts(theIntent,self.user[self.userID])
					
					if not len(entityHolder) and len(theIntent["entities"]):
						response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["fallbacks"]), entityHolder)
						extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)

					elif clearEntities:
						entityHolder = []
						response = random.choice(theIntent["responses"])
						extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)

					else:
						response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["responses"]), entityHolder)
						extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)

					if extension != None:

						classParts     = extension.split(".")
						classFolder    = classParts[0]
						className      = classParts[1]
						theEntities    = None

						if exEntities != False:
							theEntities = entities
						
						module         = __import__(classParts[0]+"."+classParts[1], globals(), locals(), [className])
						extensionClass = getattr(module, className)()
						response       = getattr(extensionClass, classParts[2])(extensionResponses, theEntities)

					return {
						"Response": "OK",
						"ResponseData": [{
							"Received": sentence,
							"Intent": classification[0][0],
							"Confidence": str(classification[0][1]),
							"Response": response,
							"Context":  [{
								"In": contextIn,
								"Out": contextOut,
								"Current": contextCurrent
							}],
							"Extension": extension,
							"Entities": entityHolder
						}]
					}

				else:

					self.user[self.userID]["context"] = ""
					contextIn, contextOut, contextCurrent = self.Context.setContexts(theIntent, self.user[self.userID])

					if fallback and fallback in theIntent and len(theIntent["fallbacks"]):
						response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["fallbacks"]), entityHolder)
						extension, extensionResponses = None, []

					else:
						response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["responses"]), entityHolder)
						extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)

					if extension != None:

						classParts     = extension.split(".")
						classFolder    = classParts[0]
						className      = classParts[1]
						theEntities    = None

						if exEntities != False:
							theEntities = entities
						
						module         = __import__(classParts[0]+"."+classParts[1], globals(), locals(), [className])
						extensionClass = getattr(module, className)()
						response       = getattr(extensionClass, classParts[2])(extensionResponses, theEntities)

					else:
						response = self.entityController.replaceResponseEntities(random.choice(theIntent["responses"]), entityHolder)

					return {
						"Response": "OK",
						"ResponseData": [{
							"Received": sentence,
							"Intent": classification[0][0],
							"Confidence": str(classification[0][1]),
							"Response": response,
							"Context":  [{
								"In": contextIn,
								"Out": contextOut,
								"Current": contextCurrent
							}],
							"Extension": extension,
							"Entities": entityHolder
						}]
					}

			else:

				contextCurrent = self.Context.getCurrentContext(self.user[self.userID])

				return {
					"Response": "FAILED",
					"ResponseData": [{
						"Received": sentence,
						"Intent": "UNKNOWN",
						"Confidence": "NA",
						"Responses": [],
						"Response": random.choice(self.confs["NLU"]["defaultResponses"]),
						"Context":  [{
							"In": "NA",
							"Out": "NA",
							"Current": contextCurrent
						}],
						"Extension":"NA",
						"Entities": entityHolder
					}]
				}
		else:	

			return {
				"Response": "FAILED",
				"ResponseData": [{
					"Status": "Training",
					"Message": "NLU Engine is currently training"
				}]
			}
class Model():
    def __init__(self):

        ###############################################################
        #
        # Sets up all default requirements
        #
        # - Helpers: Useful global functions
        # - Data: Data functions
        #
        ###############################################################

        self.Helpers = Helpers()
        self.confs = self.Helpers.loadConfigs()

        self.Data = Data()

    def createDNNLayers(self, x, y):

        ###############################################################
        #
        # Sets up the DNN layers, configuration in required/confs.json
        #
        ###############################################################

        net = tflearn.input_data(shape=[None, len(x[0])])

        for i in range(self.confs["NLU"]['FcLayers']):
            net = tflearn.fully_connected(net, self.confs["NLU"]['FcUnits'])
        net = tflearn.fully_connected(net,
                                      len(y[0]),
                                      activation=str(
                                          self.confs["NLU"]['Activation']))

        if self.confs["NLU"]['Regression']:
            net = tflearn.regression(net)

        return net

    def trainDNN(self, x, y, words, classes, intentMap):

        ###############################################################
        #
        # Trains the DNN, configuration in required/confs.json
        #
        ###############################################################

        tf.reset_default_graph()

        tmodel = tflearn.DNN(
            self.createDNNLayers(x, y),
            tensorboard_dir=self.confs["NLU"]['TFLearn']['Logs'],
            tensorboard_verbose=self.confs["NLU"]['TFLearn']['LogsLevel'])

        tmodel.fit(x,
                   y,
                   n_epoch=self.confs["NLU"]['Epochs'],
                   batch_size=self.confs["NLU"]['BatchSize'],
                   show_metric=self.confs["NLU"]['ShowMetric'])

        self.saveModelData(
            self.confs["NLU"]['TFLearn']['Data'], {
                'words': words,
                'classes': classes,
                'x': x,
                'y': y,
                'intentMap': [intentMap]
            }, tmodel)

    def saveModelData(self, path, data, tmodel):

        ###############################################################
        #
        # Saves the model data for TFLearn and the NLU engine,
        # configuration in required/confs.json
        #
        ###############################################################

        tmodel.save(self.confs["NLU"]['TFLearn']['Path'])

        with open(path, "w") as outfile:
            json.dump(data, outfile)

    def buildDNN(self, x, y):

        ###############################################################
        #
        # Loads the DNN model, configuration in required/confs.json
        #
        ###############################################################

        tf.reset_default_graph()
        tmodel = tflearn.DNN(self.createDNNLayers(x, y))
        tmodel.load(self.confs["NLU"]['TFLearn']['Path'])
        return tmodel

    def predict(self, tmodel, parsedSentence, trainedWords, trainedClasses):

        ###############################################################
        #
        # Makes a prediction against the trained model, checking the
        # confidence and then logging the results.
        #
        ###############################################################

        predictions = [[index, confidence] for index, confidence in enumerate(
            tmodel.predict(
                [self.Data.makeBagOfWords(parsedSentence, trainedWords)])[0])]
        predictions.sort(key=lambda x: x[1], reverse=True)

        classification = []
        for prediction in predictions:
            classification.append(
                (trainedClasses[prediction[0]], prediction[1]))

        return classification
class Entities():
    
    def __init__(self):

        ###############################################################
        #
        # Sets up all default requirements
        #
        # - Helpers: Useful global functions
        # - LancasterStemmer: Word stemmer
        #
        ###############################################################
        
        self.Helpers = Helpers()
        self._confs  = self.Helpers.loadConfigs()

        self.stemmer = LancasterStemmer()
        
    def restoreNER(self):

        ###############################################################
        #
        # Restores the NER model, in this case MITIE
        #
        ###############################################################
         
        if os.path.exists(self._confs["NLU"]["EntitiesDat"]):
            return named_entity_extractor(self._confs["NLU"]["EntitiesDat"])

    def parseEntities(self, sentence, ner, trainingData):

        ###############################################################
        #
        # Parses entities in intents/sentences
        #
        ###############################################################

        entityHolder   = []
        fallback       = False
        parsedSentence = sentence
        parsed         = ""

        if os.path.exists(self._confs["NLU"]["EntitiesDat"]):

            tokens   = sentence.lower().split()
            entities = ner.extract_entities(tokens)

            for e in entities:

                range     = e[0]
                tag       = e[1]
                score     = e[2]
                scoreText = "{:0.3f}".format(score)
                
                if score > self._confs["NLU"]["Mitie"]["Threshold"]:
                    
                    parsed, fallback = self.replaceEntity(
                        " ".join(tokens[i] for i in range),
                        tag,
                        trainingData)

                    entityHolder.append({
                        "Entity":tag,
                        "ParsedEntity":parsed,
                        "Confidence":str(scoreText)})

                    parsedSentence = sentence.replace(
                        " ".join(sentence.split()[i] for i in range), 
                        "<"+tag+">")

                else:
                    
                    parsed, fallback = self.replaceEntity(
                        " ".join(tokens[i] for i in range),
                        tag,
                        trainingData)

                    entityHolder.append({
                        "Entity":tag,
                        "ParsedEntity":parsed,
                        "Confidence":str(scoreText)})

                    parsed = parsedSentence
                    
        return parsed, fallback, entityHolder, parsedSentence
        
    def replaceResponseEntities(self, response, entityHolder):

        ###############################################################
        #
        # Replaces entities in responses
        #
        ###############################################################

        entities = []

        for entity in entityHolder: 
            response = response.replace("<"+entity["Entity"]+">", entity["ParsedEntity"].title())
            entities.append( entity["ParsedEntity"].title())

        return response, entities

    def replaceEntity(self, value, entity, trainingData):

        ###############################################################
        #
        # Replaces entities/synonyms
        #
        ###############################################################

        lowEntity = value.lower()
        match     = True

        if "entitieSynonyms" in trainingData:
            for entities in trainingData["entitieSynonyms"]:
                for synonyms in entities[entity]:
                    for synonym in synonyms["synonyms"]:
                        if lowEntity == synonym.lower():
                            lowEntity = synonyms["value"]
                            match = False
                            break

        return lowEntity, match 

    def trainEntities(self, mitiemLocation, trainingData):

        ###############################################################
        #
        # Trains the NER model
        #
        ###############################################################
        
        trainer     = ner_trainer(mitiemLocation)
        counter     = 0
        hasEnts = 0

        for intents in trainingData['intents']:

            i = 0
            for entity in intents['entities']:

                hasEnts = 1
                tokens  = trainingData['intents'][counter]["text"][i].lower().split()
                data    = ner_training_instance(tokens)
                data.add_entity(
                    xrange(
                        entity["rangeFrom"],
                        entity["rangeTo"]), 
                    entity["entity"])
                trainer.add(data)
                
                i = i + 1
            counter = counter + 1

        if hasEnts:
            trainer.num_threads = 4
            ner = trainer.train()
            ner.save_to_disk(self._confs["NLU"]["EntitiesDat"])