Hands On with Emotion Modelling

From OpenCog


Start by reading the Emotion Modelling page at the Hanson Robotics wiki.

Then read the Psi-theory wikipedia page

Also read the OpenCog OpenPsi page.

Grab a box of tissues and prepare to watch these tear jerkers OpenPsi - Internal Dynamics & Emotions and Emotion Graph Visualizer (held at Hong Cogathon Dec 2016). And here are the presentation slides.


Using the OpenPsi Cognitive Modulation and Emotion System

This tutorial assumes a basic understanding of OpenPsi and Psi theory.

The OpenPsi code lives here: https://github.com/opencog/opencog/tree/master/opencog/openpsi, and there is documentation about using various parts of the system in the README and code documentation. Here we will demonstrate using different parts OpenPsi's modulation and emotion system (as opposed to its motivation and action selection system). An example of a particular implementation of the modulation/emotion system for a Hanson Robotics robot and avatar can be found here: https://github.com/opencog/ros-behavior-scripting/blob/master/src/psi-dynamics.scm

Start up the scheme shell by typing "guile" in your terminal:

$ guile
GNU Guile 2.0.9
Copyright (C) 1995-2013 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.

Load OpenCog and OpenPsi modules in the scheme shell you just opened:

scheme@(guile-user)> (add-to-load-path "/usr/local/share/opencog/scm")
scheme@(guile-user)> (use-modules (opencog) (opencog openpsi))

You can also add the above to your ~/.guile file, which gets loaded automatically each time you start a scheme shell.

Load the OpenPsi dynamic modulation and emotion code by entering:

scheme@(guile-user)> (load-openpsi-in-development)

Note: this will be changed soon, so the above won't be needed.

Add a Monitored Event

OpenPsi can monitor events whose occurrence can trigger a rule to change the value of a modulator or other variable. Details and instructions on how to work with events in OpenPsi can be found at the top of the event.scm file in https://github.com/opencog/opencog/tree/master/opencog/openpsi/event.scm.

As an example, let's create an event called new-face (e.g., a new face comes into a robots field of vision) that will be monitored for occurrence by OpenPsi:

(define new-face-event (psi-create-monitored-event "new-face-event"))

Now we will create a rule that says if a new-face comes into view, this causes arousal of the agent to increase.

(define new-face->arousal
	(psi-create-interaction-rule new-face-event increased arousal .3))

(There will be more on interaction-rules in general later on.) Later we will manually simulate the appearance of a new face using the function (psi-set-event-occurance! new-face-event), but for now we just create the event and the rule.

Fire up the OpenPsi dynamics updater

Start it up with

scheme@(guile-user)> (psi-updater-run)
$2 = #<thread 139834684708608 (20d3c00)>

And we can confirm whether it is running with

scheme@(guile-user)> (psi-updater-running?)
$3 = #t

View demo log output

Assuming that logging is set to #t in opencog/updater.scm (which it is by default), the updater will output some demo information to a file called psilog.txt in the same directory that you started your scheme shell.

Open up a new terminal window and in the same directory you started the scheme shell:

$ tail -f psilog.txt

--------------------------------------------------------------------------- Loop 1
arousal: .50  pos-valence: .50  neg-valence: .50  agent power: .50  

The updater outputs the value of some of the modulator variables. A line is added for each loop in which one of the values changes.

Usually the system is changing these values based on dynamic interaction rules (a change in one variable causes a change in another), but we have not added any of these rules yet, so they are not changing.

We can change a psi value manually with

(psi-set-value! arousal .7)

and then in the psilog.txt output terminal something like:

--------------------------------------------------------------------------- Loop 75

arousal: *.70*  pos-valence: .50  neg-valence: .50  agent power: .50  

--------------------------------------------------------------------------- Loop 76

arousal: *.70*  pos-valence: .50  neg-valence: .50  agent power: .50  

--------------------------------------------------------------------------- Loop 77

arousal: *.70*  pos-valence: .50  neg-valence: .50  agent power: .50  

. . .


You'll notice in the terminal with the log output that the value of arousal is slowing decreasing. This is because the value of modulator variables slowly decay toward a baseline value, which for arousal is .5.

Adding Cyclical Rhythms and Stochastic Noise

Let's add some cyclical dynamics and noise to the arousal, pos-valence, and neg-valence variables.

; arousal ultradian rhythm params
(define arousal_B .05)
(define arousal_w .02)
(define arousal_offset (get-random-cycle-offset arousal_w))

; arousal rhythm rule
(psi-create-general-rule (TrueLink)
	(GroundedSchemaNode "scm: psi-ultradian-update")
	(List arousal (Number arousal_B) (Number arousal_w) (Number arousal_offset)))

; arousal noise param
(define arousal_noise .03)

; arousal (stochastic) noise rule
(psi-create-general-rule (TrueLink)
	(GroundedSchemaNode "scm: psi-noise-update")
	(List arousal (Number arousal_noise)))

; default utradian rhythm params
(define default_B .05)
(define default_w .02)

; default noise param
(define default_noise .015)

; pos-valence rhythm rule
(psi-create-general-rule (TrueLink)
	(GroundedSchemaNode "scm: psi-ultradian-update")
	(List pos-valence (Number default_B) (Number default_w)
		(Number (get-random-cycle-offset default_w))))

; pos-valence (stochastic) noise rule
(psi-create-general-rule (TrueLink)
	(GroundedSchemaNode "scm: psi-noise-update")
	(List pos-valence (Number default_noise)))

; neg-valence rhythm rule
(psi-create-general-rule (TrueLink)
	(GroundedSchemaNode "scm: psi-ultradian-update")
	(List neg-valence (Number default_B) (Number default_w)
		(Number (get-random-cycle-offset default_w))))

; neg-valence (stochastic) noise rule
(psi-create-general-rule (TrueLink)
	(GroundedSchemaNode "scm: psi-noise-update")
	(List neg-valence (Number default_noise)))

You can see the impact of these additions in the output terminal.

(An ultradian rhythm in biology is a recurrent cycle repeated throughout a 24 hour day, or in other words with a period less than 24 hours.)

The B parameter (for Beta) of the ultradian rhythm determines the amplitude of the wave function and the w parameter (for omega) determines the frequency of the wave in radians (iow frequency per 2*pi timesteps).

Interaction Rules

Interaction rules control the dynamic interactions among the OpenPsi internal variable. Information and instructions on how to use interaction rules is at the top of openpsi/interaction-rule.scm at https://github.com/opencog/opencog/blob/master/opencog/openpsi/interaction-rule.scm.

Let's add a few interaction rules to demonstrate.

; Internal dynamic interactions rules

; pos valence up decreases neg valence
(psi-create-interaction-rule pos-valence increased neg-valence -.2)

; neg valence up decreases pos valence
(psi-create-interaction-rule neg-valence increased pos-valence -.2)

; arousal decreases resolution
(psi-create-interaction-rule arousal changed resolution-level .5)

; arousal increases goal directedness
(psi-create-interaction-rule arousal changed goal-directedness .5)

; power decreases neg valence
(psi-create-interaction-rule power changed neg-valence -.2)

; arousal increases pos valence
(psi-create-interaction-rule arousal increased pos-valence .1)

; pos valence increases power
(psi-create-interaction-rule pos-valence changed power .2)

.. At every loop, the updater is checking to see what variables have changed and firing the rules that define the interactions among the variables.

Simulate an External Event

Remember the rule we created above that when a new face is detected by the agent, this causes arousal to increase?

(define new-face->arousal
	(psi-create-interaction-rule new-face increased arousal .3))

Let's put it into action by manually simulating a new face event.

Take a look at the output terminal and watch where the arousal value is at.

Keep your eye on that arousal value in the output terminal and at the same time in the scheme console terminal:

(psi-set-event-occurrence! new-face-event)

Arousal should have gone up.

OpenPsi provides a callback mechanism to allow for custom programming of what constitutes a given event occurrence. Documentation is at the top of openpsi/event.scm.


In Psi theory, emotion is understood as an emergent property of the modulation of perception, behavior, and cognitive processing. Emotions are interpreted as different state space configurations of cognitive modulators along with the valence (pleasure/distress) dimension, the assessment of cognitive urges, and the experience of accompanying physical sensations that result from the effects of the particular modulator settings on the physiology of the system.

Let's create some simple emotions which we will define as a state space of some OpenPsi modulator variables.

; Create emotions
(define psi-happy (psi-create-emotion "happy"))
(define psi-sad (psi-create-emotion "sad"))
(define psi-angry (psi-create-emotion "angry"))
(define psi-relaxed (psi-create-emotion "relaxed"))

For a list of all the emotions defined in the system:

scheme@(guile-user)> (psi-get-emotions)
$4 = ((ConceptNode "OpenPsi: happy")
 (ConceptNode "OpenPsi: sad")
 (ConceptNode "OpenPsi: angry")
 (ConceptNode "OpenPsi: relaxed")

The mapping between the state of the modulators (and any other desired factors) to emotions is seen as a higher level reflective process that varies for different agents, so needs to be defined for the particular agent. Here we create functions that set the emotion values, define the current emotional state as the emotion with the highest current value, and create a rule to update all these values at each time step.

; Update emotion state values and current dominant emotion state
(define (psi-update-emotion-states)
	; Using 2-dimensional Wundt model of emotions based on arousal and valence
	(define arousal (psi-get-arousal))
	(define pos-valence (psi-get-pos-valence))
	(define neg-valence (psi-get-neg-valence))

	; Multiplier for emotion level calculations
	(define emotionality-factor 1.2)
	(define e emotionality-factor)

	(define (limit value)
		(min value 1))

	(psi-set-value! psi-happy
		(limit (* pos-valence arousal e)))
	(psi-set-value! psi-angry
		(limit (* neg-valence arousal e)))
	(psi-set-value! psi-sad
		(limit (* neg-valence (- 1 arousal) e)))
	(psi-set-value! psi-relaxed
		(limit (* pos-valence (- 1 arousal) e)))


	; return atom for ExOutLink requirement

; Current emotion state
(define-public psi-current-emotion-state
	(Concept (string-append psi-prefix-str "current emotion state")))

(define (psi-set-current-emotion-state)
	; Find the emotion with the highest level and set as current emotion
	(define emotions (psi-get-emotions))
	(define strongest-emotion)
	(define highest-emotion-value -1)
		(lambda (emotion)
			(define emotion-value (psi-get-number-value emotion))
			(if (> emotion-value highest-emotion-value)
					(set! strongest-emotion emotion)
					(set! highest-emotion-value emotion-value))))
	(if (not (equal? (psi-get-value psi-current-emotion-state) strongest-emotion))
			(StateLink psi-current-emotion-state strongest-emotion)
			;(format #t "Current emotion state: ~a\n" strongest-emotion)

; Returns the current emotion state of the agent as an atom of the form
; (ConceptNode "OpenPsi: happy")
(define-public (psi-get-current-emotion)
	(psi-get-value psi-current-emotion-state))

; Rule to update emotion states each loop
(define psi-update-emotions-rule
	(psi-create-general-rule (TrueLink)
		(GroundedSchemaNode "scm: psi-update-emotion-states")

Get the current value of an emotion:

scheme@(guile-user)> (psi-get-value psi-happy)
$23 = (NumberNode "0.436650")

Or for just the number value (rather than a NumberNode):

scheme@(guile-user)> (psi-get-number-value psi-happy)
$25 = 0.437141

Get the current predominant emotional state of the agent (which we have defined as the emotion with the highest value):

scheme@(guile-user)> (psi-get-current-emotion)
$32 = (ConceptNode "OpenPsi: relaxed")

Of course these formulations of emotion are simple and can definitely be made more complex.

Real-time Graphs with the Atomspace Visualizer

Stay tuned...


1 Emotion modeling is a critical part of the OpenPsi function, which in turn drives behavior via:

the action orchestrator
the atomspace
the action selector

2 Psi-theory ...

is a systemic psychological theory covering human action regulation, intention selection and emotion
is a field of study concerned with the investigation of paranormal and psychic phenomena
has nothing to do with artificial intelligence
models the human mind as an information processing agent, controlled by a set of basic physiological, social and cognitive drives
models psychiatric tools to evaluate the moral action selection of an information processing agent, and is the cornerstone of Nick Bostrom's AI control theory
is an aspect of modern cosmist theory as espoused in Ben Goertzel's book 'A Cosmist Manifesto'

3 MicroPsi ...

extends the representations of the Psi-theory with weighted/labelled hypergraphs
utilizes deep reinforcement learning to allow for neural learning, planning and associative retrieval
extends the representations of the Psi-theory with taxonomies, inheritance and linguistic labeling
utilizes weighted/labelled hypergraphs to aid in planning and action selection
utilizes spreading activation networks to allow for neural learning, planning and associative retrieval

4 OpenPsi ...

extends the representations of the Psi-theory with weighted/labelled hypergraphs
doesn't necessarily include a specific neural net like knowledge representation
includes significant aspects of the Psi model of human motivation and emotion, inspired significantly by aspects of Joscha Bach's MicroPsi AI system
utilizes weighted/labelled hypergraphs to aid in planning and action selection
consists of two parts: a rule-base, and a mechanism for selecting rules


Priority: High

Tutorial Creators: (J Bach, Eddie (best) Amen (A bit) , Matt Ikle (he is teaching a class - will go through code)

Neural Net Knowledge Representation - perhaps including the emotion graph visualizer?

MicroPsy ?