Palabra clave: Mask

Las máscaras de acción restringen el conjunto de acciones que un concepto aprendido puede seleccionar, en función del estado actual. El enmascaramiento es útil en las aplicaciones en las que el conjunto de acciones válidas no siempre es el mismo. Por ejemplo, el cerebro no debe permitirse enrutar el trabajo a un equipo que está inactivo para el mantenimiento.

Las máscaras de acción se admiten para tipos de acción nominales, como type Action {cmd: number<Up=0, Down=1, Left=2, Right=3>}.

Uso

Para especificar una máscara, escriba una función mask y haga referencia a ella con la mask palabra clave dentro del plan de estudios para un concepto. Por ejemplo:

function MaskFunction(s: ObservableState) {
   # The mask function takes the input to the concept graph 
   # and returns a dynamic constraint on the Action type for a concept.
   return constraint Action <Dynamic Type Expression>
}

graph(input: ObservableState): ActionType {
    concept AConcept(input): ActionType {
       curriculum {
          mask MaskFunction
          ... # other curriculum elements here
       }
    }
}

Funciones mask

Las funciones mask reciben la misma entrada que el cerebro, como se especifica con la graph palabra clave . La función debe devolver una restricción de tipo dinámico que limite el tipo de acción base para el concepto.

function MaskFunction(s: ObservableState) {
   # Mask functions can use control flow, variables, etc.

   # Here, we'll restrict the bin in which to place an object based on its size
   if s.object_size < SmallSizeThreshold {
       return constraint Action {bin: number<in s.available_small_bins>}
   } else if s.object_size > LargeSizeThreshold {
       return constraint Action {bin: number<in s.available_large_bins>}
   } else {
       # No constraint
       return constraint Action {}
   }
}

El tipo de valor devuelto de una función mask se parametriza mediante el tipo estático que se restringe: Type.DynamicType<Action>. El compilador de Inkling normalmente puede deducir el tipo necesario, por lo que no es necesario definir explícitamente el tipo. Todas las rutas de acceso de devolución deben devolver el mismo tipo con parámetros.

Restricciones de tipo dinámico

Las expresiones de restricción dinámica comienzan con la palabra clave constraint y van seguidas de dos expresiones de tipo:

  • el tipo estático (sin restricciones) y
  • una restricción dinámica aplicada al primer tipo.

Una expresión de tipo dinámico usa la sintaxis de expresión de tipo existente, pero permite que algunas subexpresiones sean dinámicas en lugar de estáticas. Por ejemplo, {choice: number<in s.valid_choices>}, o {fruit: number<mask observable_state.fruit_mask>}

Hay dos tipos de restricciones dinámicas en enumeraciones nominales in y mask:

Restricción Descripción Ejemplo Detalles
in Incluir valores especificados de la enumeración number<in obs.allowedValues> Los valores que no están en la enumeración se omiten.
mask Valor booleano para cada elemento de enumeración number<mask obs.actionMask> El tamaño de la matriz debe coincidir con el tamaño de la enumeración.

Sugerencia

Para aplicar ninguna restricción, use {}.

En el caso de los tipos de acción multidimensionales, las restricciones se aplican por campo. Por ejemplo, si el tipo de acción es:

type Action {
    a: number<A=1,B=2,C=3>,
    b: number<X=1,Y=2>
}

y la restricción es:

constraint Action {a: number<in [1,2]>, b: number<in [2]>}

las acciones válidas serán {a: 1, b: 2} y {a:2, b:2}.

Importante

La restricción Mask siempre debe dar lugar a una enumeración que no sea NULL. Si todas las opciones están enmascaradas, el cerebro devolverá un error en tiempo de ejecución.

Ejemplos

Restricción mediante mask palabra clave

type Action {cmd: number<Up=0, Down=1, Left=2, Right=3>}

type ObservableState {
   x: number,
   y: number,
   # Booleans. If there's a wall, can't move in that direction
   wallAbove: number<0,1,>,  
   wallBelow: number<0,1,>,
   wallLeft: number<0,1,>
   wallRight: number<0,1,>,
}

function MaskFunction(s: ObservableState) {
    # Only move in allowed directions.
    # Note: simulator (and real world) need to ensure that at least one direction is available.

    # 1 means that action is allowed. Enumeration order in Action type is Up, Down, Left, Right.
    return constraint Action {cmd: number<mask [not s.wallAbove, not s.wallBelow, not s.wallLeft, not s.wallRight]>}
}

Restricción mediante in

type Action {cmd: number<Up=0, Down=1, Left=2, Right=3>}

type ObservableState {
   x: number,
   y: number,
   # Array of allowed directions, with -1 for unused entries
   allowedDirections: number<0..3 step 1>[4]
}

function MaskFunction(s: ObservableState) {
    # Only move in allowed directions.
    # Note: simulator (and real world) need to ensure that at least one direction is available.
    var UpOk = 1-wallAbove

    # s.allowedDirections should be an array of valid action values or placeholders
    # e.g.
    # [0,1,2,3,4] -- all allowed
    # [0,1, -1, -1] -- only Up (0) and Down (1) allowed
    return constraint Action {cmd: number<in s.allowedDirections>}
}

Uso de variables de estado en la máscara, pero no para el aprendizaje

A veces, la información necesaria para la máscara no es relevante para el aprendizaje. Por ejemplo, considere una decisión de enrutamiento que envía trabajo a una de varias máquinas de una fábrica. Si una máquina está inactiva, no se debe enviar ningún trabajo a ella, pero no es necesario aprender a evitar máquinas fuera de servicio como un concepto explícito. En su lugar, el cerebro puede aprender a seleccionar qué máquinas serían mejores y la máscara garantizará que solo se elijan las máquinas de trabajo.


type Action {
    machine: number<A=1,B=2,C=3,D=4>
}

# Mask isn't needed for learning, so leave it out
type LearningState {
   # info about the job to be done by one of the machines
   job_property_a: number,
   job_property_b: number

   # machine properties that should be used to decide which is best to use
   machine_speeds: number<1..100 step 1>[4],
   machine_costs: number<0..1>[4],
}

# Add the mask info to get the full state that will be passed to the brain
type ObservableState extends LearningState {
   # array of 4 bools: 1 if machine is available, 0 otherwise
   available_machine_mask: number<0,1,>[4],
}

graph (input: ObservableState) {
    concept RemoveMask(input): LearningState {
        programmed function(s: ObservableState): LearningState {
            # use cast to avoid writing out all the fields one by one -- works if LearningState is a subset of ObservableState
            return LearningState(s)
        }
    }

    output concept ChooseMachine(RemoveMask): Action {
        mask function(s: ObservableState) {
            return constraint Action {machine: number<mask s.available_machine_mask>}
        }
        ... # rest of curriculum definition
    }
}