Hands On with Emotion Modelling
- 1 Theory
- 2 Practice
- 2.1 Using the OpenPsi Cognitive Modulation and Emotion System
- 2.2 Add a Monitored Event
- 2.3 Fire up the OpenPsi dynamics updater
- 2.4 View demo log output
- 2.5 Adding Cyclical Rhythms and Stochastic Noise
- 2.6 Interaction Rules
- 2.7 Simulate an External Event
- 2.8 Emotions
- 2.9 Real-time Graphs with the Atomspace Visualizer
- 3 Quiz
- 4 Notes
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. scheme@(guile-user)>
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:
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 . . . etc.
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 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:
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))) (psi-set-current-emotion-state) ; return atom for ExOutLink requirement (True) ) ; 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) (for-each (lambda (emotion) (define emotion-value (psi-get-number-value emotion)) (if (> emotion-value highest-emotion-value) (begin (set! strongest-emotion emotion) (set! highest-emotion-value emotion-value)))) emotions) (if (not (equal? (psi-get-value psi-current-emotion-state) strongest-emotion)) (begin (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") (List)))
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
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?