Frameworks y ontologías (Parte 2)

Hace ya un tiempo que no me sentaba a escribir en el blog, la verdad que por motivos de tiempo, trabajo, etc. Pero era hora de que cumpla con lo prometido y termine la segunda entrega del tutorial de Ontologías, sin más preambulos aqui les va ...


Frameworks y Ontologías (Parte 2)

En la Parte 1 de este tutorial sobre tecnologías circundantes al desarrollo de aplicaciones basadas en Ontologías introdujimos una serie pequeña de conceptos teóricos sobre los distintos lenguajes que existen en el mercado actual, ejemplos de éstos son: RDF, RDF Schema, OWL Lite, OWL DL y por último OWL Full. Mencionamos las distintas herramientas que existen para la manipulación de éstos lenguajes: Jena, Pellet, OWL - API, etc.
Comenzamos con una Introducción a Jena, uno de los frameworks más utilizados, y listamos el conjunto de APIs que lo conforman.
Mencionamos como trata el polimorfismo en Java, y describimos algunos conceptos básicos del funcionamiento del framework.
En esta segunda parte seguiremos explorando Jena y como trata los modelos, nos centraremos en algunos ejemplos sencillos.
Veremos cómo poder cargar nuestras ontologías, como cargar modelos, como asociar uno u otro razonador, como persistir un modelo de ontologías y como cargarlo desde la base de datos.

Comenzaremos con lo básico, supongamos que contamos con nuestra ontología la cual está guardada en nuestro disco rígido en formato owl y queremos poder manipularla, ¿cómo hacemos?
Para dar respuesta a esto en primer lugar debemos contar con un entorno de desarrollo, en particular utilizo Netbeans para el desarrollo en java y el jdk 1.6.
Creamos un proyecto en el IDE, bajamos las librerías de Jena (en el caso que no las tengamos) y las adherimos al proyecto.

Para nuestro simple ejemplo vamos a definir la clase OntLoader de la siguiente manera:

Código
GeSHi (java):
public class OntLoader {
   private File file = null;
   private OntModel model = null;
   public OntLoader(File file) {
   java.io.InputStream input = null;
   try {
       this.file = file;
       //Creación del modelo, que contendrá la representación de la ontología
       model = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM );
       //Este input es la ontología en formato owl
       input = new FileInputStream(file);
       //Carga del modelo con la ontología en el input
       model.read(input, "");
       } catch (FileNotFoundException ex) {
           Logger.getLogger("global").log(Level.SEVERE, null, ex);
       } finally {
           try {
               input.close();
           } catch (IOException ex) {
               Logger.getLogger("global").log(Level.SEVERE, null, ex);
           }
       }
   }
}
Created by GeSHI 1.0.7.18

El código anterior muestra de manera sencilla la carga de una ontología en un modelo Jena, en el constructor de la clase nos llega un objeto de la clase File, el cual representa nuestro archivo owl.

La siguiente línea model = ModelFactory.createOntologyModel( OntModelSpec.OWL_DL_MEM ) significa que se va a crear un modelo en memoria y basado en OWL-DL.
Si la linea hubiese sido model = ModelFactory.createOntologyModel(), el modelo se creara con la configuración por defecto que se lista a continuación:

    * Lenguaje OWL-FULL
    * Almacenaje en Memoria
    * Inferencia basada en RDF – Schema

Existen algunas variantes para la creación de nuestro modelo dependiendo del tipo de raciocinio o el perfil del lenguaje que necesitemos. Esto puede ser especificado utilizando constantes definidas en la clase OntModelSpec, las cuales definen ciertas configuraciones pre-establecidas haciendo un wrapper, de las URIs que representan los lenguajes (buscados en la clase ProfileRegister), el tipo de almacenamiento y el razonador. Si precisamos una configuración propia, podemos crear una instancia de OntModelSpec y a través de setters establecer los valores apropiados.

Pero hasta el momento no tenemos nuestra ontología cargada, es el método read() el que se encarga de esta tarea, como lo demarca la siguiente línea model.read(input, "");
Existen algunas variantes del método read() que a continuación se enumeran:

    * read( String url )
    * read( Reader reader, String base )
    * read( InputStream reader, String base )
    * read( String url, String lang )
    * read( Reader reader, String base, String Lang )
    * read( InputStream reader, String base, String Lang )

Cuando cargamos nuestro documento en el modelo, por defecto se cargan todos los import que nuestra ontología realiza. Es decir todo import que se realice sera cargado como un sub-modelo del modelo que se esta construyendo. Este import es recursivo, pero existe un chequeo para evitar ciclos.
Cada modelo tiene asociado un Document Manager que es el encargado de manejar todos los documentos relacionados, por conveniencia existe un Document Manager global que es compartido por todos los modelos y es accesible a través de OntDocumentManager.getInstance(). Para muchos casos, cambiando las configuraciones generales mencionadas anteriormente es suficiente, pero para casos específicos uno puede crear un Document Manager particular y setearlo a la instancia de OntModelSpec que será pasada al Factory que nos creará el modelo.

Código
GeSHi (java):
OntDocumentManager mgr = new OntDocumentManager();
// ...... codigo de usuario .......
OntModelSpec s = new OntModelSpec( OntModelSpec.RDFS_MEM );
s.setDocumentManager( mgr );
OntModel m = ModelFactory.createOntologyModel( s );
Created by GeSHI 1.0.7.18

Existen dos maneras de configurar un OntDocumentManager, una es programáticamente y la otra es a través de un archivo de configuración (policy file), el cual está expresado en RDF. La implementación de OntDocumentManager tiene una lista de URLs donde busca estos archivos, el primero que resuelve es el que cargara como configuración por defecto. Si es necesario se puede cambiar la ruta donde busca la implementación con el método setMetadataSearchPath().
Existe mucha configuración relacionada al modelo, por ejemplo la manera de tratar los imports si se procesan o no, como resolver en el caso que la URL no sea encontrada, como trabajar localmente y cómo manejar el caching de modelos cargados con imports. Todo esto es simple y puede encontrarse en forma detallada en la documentación de Jena o en el javadoc del API.

Para seguir detallando un poco más el API de Jena e ir profundizando en sus funcionalidades, pasaremos a describir la interface OntResource.
Todas las clases del API de OWL de Jena que representan un valor en la ontología implementan o extienden esta interface. OntResource, a su vez extiende la interace Resource (RDF).
Algunos atributos comunes en la ontología son representados como métodos en la interface, por ejemplo

    * versionInfo
    * comment
    * label
    * seeAlso
    * isDefinedBy
    * sameAs
    * differentFrom

y para cada un de ellos sus patrones correspondientes get, add, is, has, list, remove.

Para terminar con esta entrega damos un pequeño ejemplo de como utilizar ciertos métodos de la interface, tomamos el ejemplo de la página de Jena para el caso de creación de un editor de ontologías y listar los tipos inferidos y los tipos afirmados (inferred vs asserted):

Código
GeSHi (java):
//creación del modelo base
String SOURCE = "http://www.eswc2006.org/technologies ontology";
String NS = SOURCE + "#";
OntModel base = ModelFactory.createOntologyModel( OWL_MEM );
base.read( SOURCE, "RDF/XML" );
//creacion del modelo inferido a travez del modelo base
OntModel inf = ModelFactory.createOntologyModel( OWL_MEM_MICRO_RULE_INF, base );
//creacion de un individuo
OntClass paper = base.getOntClass( NS + "Paper" );
Individual p1 = base.createIndividual( NS + "paper1", paper );
//Listado de los tipos asertados
for (Iterator i = p1.listRDFTypes(); i.hasNext(); ){
  System.out.println( p1.getURI() + " is asserted in class " + i.next() );
}
//Listado de los tipos inferidos
p1 = inf.getIndividual( NS + "paper1" );
for (Iterator i = p1.listRDFTypes(); i.hasNext(); ){
  System.out.println( p1.getURI() + " is inferred to be in class " + i.next() );
}
 
Created by GeSHI 1.0.7.18
En la próxima entrega veremos en más detalle el API de OWL de Jena, algunos ejemplos sencillos que sirvan de introducción y de guía a los que se inician en el tema. Cumpliremos con la promesa de la persistencia y nos introduciremos en el API de inferencia. Cuando culminemos con el API de Jena, comenzaremos a ver OWL-API y luego una introducción a Pellet.

Hasta la próxima….

Comentarios

Entradas más populares de este blog

¿Qué es GWT?