<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2024-10-05T11:13:36+00:00</updated><id>/feed.xml</id><title type="html">Probabilistic Soft Logic</title><subtitle>Probabilistic soft logic (PSL) is a machine learning framework for developing probabilistic models. PSL models are easy and fast, you can define them using a straightforward logical syntax and solve them with fast convex optimization. PSL has produced state-of-the-art results in many areas spanning natural language processing, social-network analysis, and computer vision. The PSL framework is available as an Apache-licensed, open source project on GitHub with an active user group for support.</subtitle><entry><title type="html">PSL 2.3.0 Release</title><link href="/blog/2023/01/01/psl-2.3.0-release.html" rel="alternate" type="text/html" title="PSL 2.3.0 Release" /><published>2023-01-01T07:00:00+00:00</published><updated>2023-01-01T07:00:00+00:00</updated><id>/blog/2023/01/01/psl-2.3.0-release</id><content type="html" xml:base="/blog/2023/01/01/psl-2.3.0-release.html"><![CDATA[<h1 id="todo-switch-code-views-from-develop-to-230">TODO: Switch code views from develop to 2.3.0</h1>

<p>We are happy to announce the release of PSL version 2.3.0!
We have made great improvements to PSL in the areas of optimization and infrastructure.
In this changelog, you will find a list of the major changes in 2.3.0 as well as information on migrating from 2.2.2.
2.3.0 is scheduled to be the last minor release before our next major release, PSL 3.0.0.
PSL 3.0.0 will contain breaking including dropping Java 7 support.</p>

<p>For those of you that learn better by example, check out the <a href="https://github.com/linqs/psl-examples">PSL examples repository</a>.
Tags are included in the PSL examples repository that track specific versions of PSL to help you ensure the examples are using the same version of PSL as you.</p>

<p>Both the <a href="https://github.com/linqs/psl">primary PSL repository</a> as well as the <a href="https://github.com/linqs/psl-examples">PSL examples repository</a> are changing their branching scheme.
Previously, the default branch <code class="language-plaintext highlighter-rouge">master</code> tracked the latest stable PSL release, while the <code class="language-plaintext highlighter-rouge">develop</code> branch contained all active development.
Going forward, only one branch, <code class="language-plaintext highlighter-rouge">main</code>, will be used, which will contain all active development.
Tags will continue to be used to track releases (major and minor).</p>

<ul>
  <li><a href="#infrastructure-improvements">Infrastructure Improvements</a>
    <ul>
      <li><a href="#ci">CI</a></li>
      <li><a href="#testing">Testing</a></li>
      <li><a href="#systems-improvements">Systems Improvements</a></li>
    </ul>
  </li>
  <li><a href="#psl-interfaces">PSL Interfaces</a>
    <ul>
      <li><a href="#cli-run-script-updates">CLI Run Script Updates</a></li>
      <li><a href="inference-configuration-specification">Inference Configuration Specification</a></li>
      <li><a href="#psl-runtime">PSL Runtime</a></li>
    </ul>
  </li>
  <li><a href="#pipeline-method-improvements">Pipeline Method Improvements</a>
    <ul>
      <li><a href="#optimization">Optimization</a></li>
      <li><a href="#weight-learning">Weight Learning</a></li>
      <li><a href="#evaluation">Evaluation</a></li>
    </ul>
  </li>
  <li>
    <p><a href="#online-psl">Online PSL</a></p>
  </li>
  <li><a href="#miscellanea">Miscellanea</a>
    <ul>
      <li><a href="#new-logging-options-infrastructure">New Logging Infrastructure</a></li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="infrastructure-improvements">Infrastructure Improvements</h2>

<p>PSL 2.3.0 comes with various improvements to our infrastructure and software quality.</p>

<h3 id="ci">CI</h3>

<p>Commit: <a href="https://github.com/linqs/psl/commit/d10e2f4fcb7bc745bb7d673f8ea014a62a35f797"><em>d10e2f4f</em></a>,</p>

<p>Continuous integration (CI) for PSL has moved from <a href="https://app.travis-ci.com/github/linqs/psl">Travis</a> to <a href="https://github.com/linqs/psl/actions">Github Actions</a>.
In addition, many new checks have been added to the PSL CLI, including:</p>
<ul>
  <li>Lint checks on our Java codebase. <a href="https://github.com/linqs/psl/commit/35062c03dfa686be7d0ba793a8d1d2fcb7457622"><em>35062c03</em></a></li>
  <li>Updated PyPi versioning to create unique identifiers for each build. <a href="https://github.com/linqs/psl/commit/1045d4bdcee62eef98125e6b880de33c74411680"><em>1045d4bd</em></a></li>
  <li>Non-release builds are pushed to the Sonatype snapshot repository (Java artifacts) and <a href="https://test.pypi.org/project/pslpython/">test.pypi.org</a>.</li>
  <li>Java versions 8, 11, 16, and 17 are tested. <a href="https://github.com/linqs/psl/commit/49180f6588938ffcbe9307ffb5bbfdd3ffd4a8a4"><em>49180f65</em></a></li>
  <li>Python versions 3.7, 3.8, and 3.9 are tested. <a href="https://github.com/linqs/psl/commit/49180f6588938ffcbe9307ffb5bbfdd3ffd4a8a4"><em>49180f65</em></a></li>
</ul>

<h3 id="testing">Testing</h3>

<p>Along with improvements to our CI, we have made various improvements to make PSL’s testing more robust and accessible for new contributors:</p>
<ul>
  <li>Moved PSL test utils to their own package. <a href="https://github.com/linqs/psl/commit/41800c486fa7369c3dd9b75dce099fd72850ddb8"><em>41800c48</em></a></li>
  <li>Added a common test class, <a href="https://github.com/linqs/psl/blob/develop/psl-core/src/test/java/org/linqs/psl/test/PSLBaseTest.java">PSLBaseTest</a> that all PSL core tests can derive from. This base test includes common setup and cleanup functionality. <a href="https://github.com/linqs/psl/commit/88d828290c76bbc3eb264f36a902d4c6bf2372e2"><em>88d82829</em></a></li>
  <li>Added standard Junit assertions to the base PSL test class. Now child tests don’t need to import the assertions or configure them (like when comparing floating point numbers). <a href="https://github.com/linqs/psl/commit/1d6e681f67a102d4a2e8ec04b4484016c85844e5"><em>1d6e681f</em></a>,</li>
  <li>Incorporated the functionality from PSLTest into PSLBaseTest. <a href="https://github.com/linqs/psl/commit/00cf883e0a1662a6dfedcefe988783c584014561"><em>00cf883e</em></a></li>
</ul>

<h3 id="systems-improvements">Systems Improvements</h3>

<p>In our continuous dedication to performant software, PSL 2.3.0 includes several systems-level improvements the affect both speed and memory usage:</p>
<ul>
  <li>Grounding will perform additional checks when instantiating rules to avoid creating trivial ground rules. <a href="https://github.com/linqs/psl/commit/fc898bfda443905ac2d4b604ef56b8758f7ba35b"><em>fc898bfd</em></a></li>
  <li>Grounding will bypass querying the database for atoms from closed predicates that it can safely infer a value for. <a href="https://github.com/linqs/psl/commit/befbf5ab2d374409ec5925b0071c87fee6703600"><em>befbf5ab</em></a></li>
  <li>Grounding will skip instantiating certain closed atoms that are deemed useless. <a href="https://github.com/linqs/psl/commit/ee12f8f76e1be06150e1fc5d1507b575185ae6f0"><em>ee12f8f7</em></a></li>
  <li>Batch allocation of ground rules is modified to optimize time spent allocating structures. <a href="https://github.com/linqs/psl/commit/b7f98282b0318d0dd8684acf919a35937fe9a91a"><em>b7f98282</em></a></li>
  <li>Numeric constructors are replaced with calls to <code class="language-plaintext highlighter-rouge">valueOf()</code> of the appropriate type. <a href="https://github.com/linqs/psl/commit/0a7d6c6ff786b14a46c1732ddf5d403ee53bb71d"><em>0a7d6c6f</em></a></li>
</ul>

<h2 id="psl-interfaces">PSL Interfaces</h2>

<p>The PSL 2.2.1 release includes non-breaking changes to one PSL interface and a brand new mid-level interface.</p>

<h3 id="cli-run-script-updates">CLI Run Script Updates</h3>

<p>Commit (psl-examples): <a href="https://github.com/linqs/psl-examples/commit/a8c01a642811bb7091a881d29d59b98500432473"><em>a8c01a64</em></a></p>

<p>The PSL run scripts (<code class="language-plaintext highlighter-rouge">run.sh</code>) provided with the <a href="https://github.com/linqs/psl-examples">PSL examples</a> now includes a variable (<code class="language-plaintext highlighter-rouge">RUN_SCRIPT_VERSION</code>) denoting the version of the run script. This version is independent of the PSL version and provides a mechanism for checking if a script (which may have been copied to your own model) is out-of-date.</p>

<p>Additionally, the CLI run scripts will now work with snapshot builds even if PSL has not been built on the local machine. The script will first check for a locally built instance of PSL matching the specified version, and if that does not exist the script will fetch a matching snapshot build from our test servers. To update the build/jar being used, you must delete the existing jar to force a re-fetch.</p>

<h3 id="inference-configuration-specification">Inference Configuration Specification</h3>

<p>Commit: <a href="https://github.com/linqs/psl/commit/105bde525ebc4228aee18b996f05e7b6b2a1af9a"><em>105bde52</em></a></p>

<p>Shortcuts are now provided to configure inference settings. Instead of individually specifying reasoners, term stores, term generators, etc.; inference classes are provided that already have the required configuration. For example, to run <a href="https://linqs.github.io/linqs-website/publications/#id:srinivasan-aaai20b">Tandem Inference (TI)</a> using the old method (which still works), you would include the options:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--infer -D inference.reasoner=SGDReasoner -D inference.termstore=SGDStreamingTermStore -D inference.termgenerator=SGDTermGenerator
</code></pre></div></div>
<p>Using the new method, you would just use:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--infer SGDStreamingInference
</code></pre></div></div>

<p>The available inference configurations can be <a href="https://github.com/linqs/psl/tree/develop/psl-core/src/main/java/org/linqs/psl/application/inference/mpe">viewed here</a>.</p>

<h3 id="psl-runtime">PSL Runtime</h3>

<p>Commit: <a href="https://github.com/linqs/psl/commit/d86d0a8c5a138f275b1aa38022a09efcc1b816b8"><em>d86d0a8c</em></a></p>

<p>PSL 2.3.0 introduces a new mid-level interface to PSL: <a href="https://github.com/linqs/psl/blob/develop/psl-java/src/main/java/org/linqs/psl/runtime/Runtime.java">the PSL Runtime</a>.</p>

<p>The motivation behind the runtime is to provide a single platform capable of running a full PSL pipeline (e.g. parsing rules, loading data, grounding, inference, and evaluation) that is more programmatically accessible than the CLI.</p>

<p>As a “mid-level” interface, the PSL Runtime is not indented for the common PSL use cases, but rather for the people building new PSL interfaces or people that need access to PSL internals while still easily running PSL pipelines. Most users are recommended to keep using the CLI or Python interface.</p>

<h2 id="pipeline-method-improvements">Pipeline Method Improvements</h2>

<p>PSL 2.3.0 includes many improvements to some of our core pipeline functionality, namely optimization/reasoning, weight learning, and evaluation.</p>

<h3 id="optimization">Optimization</h3>

<ul>
  <li>Reworked the breaking conditions for all optimizers. Where applicable, solution feasibility has been made a higher priority when determining when to stop. Variable movement tracking is also included as a break condition. Reasoners also now have the option (<code class="language-plaintext highlighter-rouge">reasoner.runfulliterations</code>) to run until the maximum number of iterations is reached regardless of any other stopping criteria. [<a href="https://github.com/linqs/psl/commit/378d676cc6b0292a18c01c7a920dda4a806f1632"><em>378d676c</em></a>, <a href="https://github.com/linqs/psl/commit/640ecba612907e7844d7c80f52ae74540172bf83"><em>640ecba6</em></a>, <a href="https://github.com/linqs/psl/commit/728e18997f1b26d825cda60db9f229f3e8042b89"><em>728e1899</em></a>, <a href="https://github.com/linqs/psl/commit/130fb0005759805d52e2ab683ec432d4b56f6153"><em>130fb000</em></a>]</li>
  <li>Removed direct support for boolean reasoning (MaxWalkSat and MCSat). [<a href="https://github.com/linqs/psl/commit/4f4420a09506900781a269a1bfd948970530fabd"><em>4f4420a0</em></a>]</li>
  <li>Added more ways atoms can be initialized for inference (1.0 and 0.5). [<a href="https://github.com/linqs/psl/commit/b786a36519f1e15e90760fe25d87c49f4d89bc2b"><em>b786a365</em></a>]</li>
  <li>SGD-based inference methods will now relax their hard constraints into soft ones. The chosen weight is the largest weight seen multiplied by a constant (<code class="language-plaintext highlighter-rouge">inference.relax.multiplier</code>). The choice of a quadratic or linear relaxation is chosen by the <code class="language-plaintext highlighter-rouge">inference.relax.squared</code> option. [<a href="https://github.com/linqs/psl/commit/e4aa914a776fba28f3d14bfd168d24f9f4d5df76"><em>e4aa914a</em></a>]</li>
  <li>Added weight normalization (<code class="language-plaintext highlighter-rouge">inference.normalize</code>) and relaxation (<code class="language-plaintext highlighter-rouge">inference.relax</code>) for all rules and inference methods. Weight normalization converts all weights to be in [0, 1] by dividing all weights by the largest weight. [<a href="https://github.com/linqs/psl/commit/6e213e9c3624a289055e68654d0109338e9337ac"><em>6e213e9c</em></a>]</li>
  <li>Improved the logging of SGD and DCD to match the semantics and style of ADMM. [<a href="https://github.com/linqs/psl/commit/3fc335411d5eab0baa1c5ac8a0e002a1d541e7f2"><em>3fc33541</em></a>, <a href="https://github.com/linqs/psl/commit/438f38d256737fa368e4764da3ee5ecdc9e244dc"><em>438f38d2</em></a>]</li>
  <li>Added the ability to run an Evaluator between rounds of optimization using the <code class="language-plaintext highlighter-rouge">reasoner.evaluate</code> option. Evaluators already selected for the evaluation stage (e.g., the <code class="language-plaintext highlighter-rouge">--eval</code> CLI option) will be used. [<a href="https://github.com/linqs/psl/commit/e3c9f6798fc78d75ea76bf34ebaf910d2d7f0673"><em>e3c9f679</em></a>]</li>
  <li>SGD now uses the lowest objective variable values as its solution. Since SGD steps are not guaranteed to decrease the MAP objective and the objective computation used to detect convergence is delayed one iteration, the best solution may not be the final state. [<a href="https://github.com/linqs/psl/commit/d2c866db9ae7d658949282bdde9184efab08f184"><em>d2c866db</em></a>]</li>
  <li>SGD now uses first-order optimality conditions to measure convergence of stochastic gradient descent reasoning. [<a href="https://github.com/linqs/psl/commit/edc96214ed9848eaa3c8360ab3358d54306fd90a"><em>edc96214</em></a>]</li>
</ul>

<h3 id="weight-learning">Weight Learning</h3>

<p>Older methods that are now outperformed in both speed and quality of answer by more modern methods have been removed. These removed methods includes all EM-based methods [<a href="https://github.com/linqs/psl/commit/ecdafb3ea0f6d31953243cef72eabe5ce898e6ca"><em>ecdafb3e</em></a>] and the maximum pseudo-likelihood learner [<a href="https://github.com/linqs/psl/commit/15c26eeaa4f44d29d4125c71e944334f4de6d945"><em>15c26eea</em></a>]. The weight sampling method used in search-based learners has been improved by sampling from a hypersphere and Dirichlet distribution [<a href="https://github.com/linqs/psl/commit/370462f35854ffa71eb6c3432b2a5471033fdb08"><em>370462f3</em></a>]. This allows these methods to get a better representation of the search space. For an overview of weight learning (theory and methods) in PSL, we recommend the paper <a href="https://linqs.github.io/linqs-website/publications/#id:srinivasan-mlj21">A Taxonomy of Weight Learning Methods for Statistical Relational Learning</a>.</p>

<h3 id="evaluation">Evaluation</h3>

<p>PSL 2.3.0 includes some minor changes to evaluation, including the renaming of the RankingEvaluator to the <a href="https://github.com/linqs/psl/blob/develop/psl-core/src/main/java/org/linqs/psl/evaluation/statistics/AUCEvaluator.java">AUCEvaluator</a> <a href="https://github.com/linqs/psl/commit/5b5f43de7a23db3c20ae8a40ae81a1dea2cf8336"><em>5b5f43de</em></a> and a rework of the <a href="https://github.com/linqs/psl/blob/develop/psl-core/src/main/java/org/linqs/psl/application/learning/weight/TrainingMap.java">TrainingMap</a> to more rigorously handle all the possible state of tracked variables (i.e., the observed/unobserved status of training/truth variables). Evlaution will use the reworked training map in addition to configuration options when deciding which atoms to include in evaluation. The <code class="language-plaintext highlighter-rouge">eval.closetruth</code> option applies the closed-world assumption to truth atoms and includes target atoms that have no truth atom specified in evaluation, while the <code class="language-plaintext highlighter-rouge">eval.includeobs</code> option includes observed atoms from the target database in evaluation. [<a href="https://github.com/linqs/psl/commit/39a7cdca4c2a5a475fa0ca46faee9b1e62a7db2a"><em>39a7cdca</em></a>, <a href="https://github.com/linqs/psl/commit/2c1c8b6cdf758baa452aaaa76dc5dc24e78cc38c"><em>2c1c8b6c</em></a>].</p>

<h2 id="online-psl">Online PSL</h2>

<p>commit 8f80c384b31b13ef7e0e47a1e9984c3d61379c84
Date:   Wed Jun 30 09:00:39 2021 -0700</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Inference Optimization (#283)

Delayed objective calculation in the SGD and DCD reasoners.
Saves non-optimizing passes through the data.
Objective change used as stopping criterion is normalized by the number of terms.
Future functionality may modify number of ground terms.
Removal of learning rate as an objective term instance variable.
Saves memory as the learning rate is common accross potentials and now managed by the reasoner.
Added option for setting the learning schedule for SGD inference.
Added option for taking coordinate updates during SGD steps.
Implementation of adagrad and adam in the SGD reasoner.
Improves convergence of inference.
Moved "minimization" (which is actually taking a gradient step) out of DCD/SGD terms and into the reasoner.
</code></pre></div></div>

<p>commit 586cbc755f140aa0d0958b4448e521f228d2d466
Date:   Fri Jul 2 09:16:25 2021 -0700</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Introduce online messages. (#308)

Introduce online messages.
Online messages are serializable objects for online client-server communication.
The testing infrastructure for online term stores and reasoners relies on the definition of these objects.
</code></pre></div></div>

<p>commit 4265741bd0dfa095e8ac16eff47f1ba14f648902
Date:   Tue Jul 6 15:06:36 2021 -0700</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Introduce online responses and model information messages. (#309)

Introduce online responses and model information messages.
Action status messages are sent to the client after the online psl server executes an online action.
QueryAtomResponses are sent to the client when it sends a QueryAtom action.
ModelInformation is sent when the client initially establishes a connection to the server.
</code></pre></div></div>

<p>commit 9e406ea18412195a7ecf88adc04a71c065f3a87c
Date:   Sat Aug 7 09:27:04 2021 -0700</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Introduce OnlineInference applications, OnlineClients, and OnlineServers. (#310)

Introduce OnlineInference applications, OnlineClients, and OnlineServers.
This version of OnlineInference only supports Exits and Stops.
Online test utilities and an SGDOnlineInference application test are also introduced.
Currently, the testing infrastructure only tests to see that SGDOnlineInference applications start, will accept client connections, and will shut down cleanly.
</code></pre></div></div>

<p>commit a4bfb8e21bb405c0594b9d398dfb11f93e1d2668
Date:   Wed Aug 18 08:49:55 2021 -0700</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Support addAtom actions in online inference applications. (#313)

Support addAtom actions in online inference.
This requires the introduction of online term stores and online grounding iterators.
</code></pre></div></div>

<p>commit 5a9cd7c6bced96094dbad1cb4cccac0af9d9d929
Date:   Thu Sep 16 16:15:00 2021 -0700</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Add support for remaining model and control actions.  (#315)

Add support for remaining model and control actions. This includes DeleteAtom, ObserveAtom, UpdateObservation, and WriteInferredPredicates actions.
</code></pre></div></div>

<p>commit d7a6af1c8539ee1859357f4a6e6146bb33669a38
Date:   Tue Nov 2 09:08:56 2021 -0700</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Support for online rule actions.  (#319)

Support for online rule actions.
This pull request contains code changes that were necessary for supporting online rule actions.
Notable changes include:
- Hashcodes for abstract rules are no longer identity hashcodes. They are functions of the parameters defining the rules and are provided as an argument to the constructor of abstract rules. Fake rules now have a hashcode, 0. This change ensures that rules in rule actions have the same hash on the client and server.
- Deactivating and deleting rules can throw off the term count that is important in detecting convergence, both the cache iterators and grounding iterators now keep track of this count so the reasoner has the most up-to-date count when it needs it.
</code></pre></div></div>

<p>commit ed404b8079a00175b9584bd64bdd6d6ead4d6ada
Date:   Mon Nov 8 14:32:04 2021 -0800</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Add OnlinePSL grammar and OnlineActionLoader class.  (#323)

Add OnlinePSL grammar and OnlineActionLoader class to parse user-provided online commands.
</code></pre></div></div>

<p>commit e290599ce36ae566c26ec606dcb399830ce4369b
Date:   Thu Nov 18 10:40:05 2021 -0800</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Online delete atoms. (#326)

Fix an issue where deleting the existing atom during an add always provides null to the online termstore and this could lead to duplicated terms.
Now, the return value of the deleteAtom call is used to delete atoms in Online terms.
</code></pre></div></div>

<p>commit 9be3bd40cc30e4ac2ad8b19fac49cdc07d03e9ef
Date:   Fri Nov 26 08:13:59 2021 -0800</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Online action interface.  (#325)

Introduce the online action interface, an interface for users to provide online actions via stdin.
</code></pre></div></div>

<h2 id="misc">Misc</h2>

<h3 id="new-loggingoptions-infrastructure">New Logging/Options Infrastructure</h3>

<p>Commit: <a href="https://github.com/linqs/psl/commit/bcf8fe450d24c1024b28894dba250df35154d55f"><em>bcf8fe45</em></a></p>

<p>In an effort to improve PSL’s logging infrastructure, we have updated our Log4J dependency to the latest version. Moving fully to Log4J 2 and getting rid of the Log4J 1-to-2 bridge as well as the Log4J 1 configurations. This allows us to more easily stay up-to-date with any Log4J updates and stay ahead of any security issues.</p>

<p>This effort also includes a new <a href="https://github.com/linqs/psl/blob/develop/psl-core/src/main/java/org/linqs/psl/util/Logger.java">logging utility class</a> that acts as both a logger and logging configuration. When used statically, this class provides an interface into logging configuration including getting a new logger and setting the logging level. When used as an object, this class provides standard logging methods that pass through to a Log4J logger. Using this class, PSL developers only need to import one logging resource while having the specifics abstracted.</p>

<p>Commit: <a href="https://github.com/linqs/psl/commit/6332b92cf0936eeae32a72aca4973f6b48879eda"><em>6332b92c</em></a></p>

<p>All configuration options have been moved to a centalized locations (<a href="https://github.com/linqs/psl/blob/develop/psl-core/src/main/java/org/linqs/psl/config/Options.java">the Options class</a>). This allows uniform standards on the creation and use of configuration options.</p>]]></content><author><name>Eriq Augustine</name></author><category term="release" /><category term="v2.3.0" /><summary type="html"><![CDATA[TODO: Switch code views from develop to 2.3.0]]></summary></entry><entry><title type="html">PSL 2.2.1 Release</title><link href="/blog/2019/12/06/psl-2.2.1-release.html" rel="alternate" type="text/html" title="PSL 2.2.1 Release" /><published>2019-12-06T23:00:00+00:00</published><updated>2019-12-06T23:00:00+00:00</updated><id>/blog/2019/12/06/psl-2.2.1-release</id><content type="html" xml:base="/blog/2019/12/06/psl-2.2.1-release.html"><![CDATA[<p>We are happy to announce the release of PSL version <code class="language-plaintext highlighter-rouge">2.2.1</code>!
We have made great improvements to PSL in the areas of usability and performance.
In this changelog, you will find a list of the major changes in <code class="language-plaintext highlighter-rouge">2.2.1</code> as well as information on migrating from <code class="language-plaintext highlighter-rouge">2.1.0</code>.</p>

<p>For those of you that learn better by example, check out the <a href="https://github.com/linqs/psl-examples">PSL examples repository</a>.
The <code class="language-plaintext highlighter-rouge">master</code> branch is always compatible with the most resent stable release,
while the <code class="language-plaintext highlighter-rouge">develop</code> branch stays up-to-date with our development work.</p>

<ul>
  <li><a href="#infrastructure">Infrastructure</a>
    <ul>
      <li><a href="#artifacts-moved-to-maven-central">Artifacts Moved to Maven Central</a></li>
      <li><a href="#wiki-hosted-on-psllinqsorg">Wiki Hosted on psl.linqs.org</a></li>
      <li><a href="#api-reference-hosted-on-psllinqsorg">API Reference Hosted on psl.linqs.org</a></li>
      <li><a href="#development-repo-now-linqspsl">Development Repo Now linqs/psl</a></li>
      <li><a href="#issues-moved-to-linqspsl">Issues Moved to linqs/psl</a></li>
    </ul>
  </li>
  <li><a href="#psl-interfaces">PSL Interfaces</a>
    <ul>
      <li><a href="#new-python-interface">New Python Interface</a></li>
      <li><a href="#new-java-interface">New Java Interface</a></li>
      <li><a href="#groovy-interface-deprecated">Groovy Interface Deprecated</a></li>
    </ul>
  </li>
  <li><a href="#cli-improvements">CLI Improvements</a>
    <ul>
      <li><a href="#functional-predicates">Functional Predicates</a></li>
      <li><a href="#multiple-evaluators">Multiple Evaluators</a></li>
      <li><a href="#output-ground-rules">Output Ground Rules</a></li>
      <li><a href="#skip-database-commit">Skip Database Commit</a></li>
      <li><a href="#remove-extra-quoting">Remove Extra Quoting</a></li>
      <li><a href="#runsh-takes-arguments">run.sh Takes Arguments</a></li>
    </ul>
  </li>
  <li><a href="#performance">Performance</a>
    <ul>
      <li><a href="#reduced-memory">Reduced Memory</a></li>
      <li><a href="#smaller-types">Smaller Types</a></li>
      <li><a href="#matrix-operations">Matrix Operations</a></li>
      <li><a href="#streaming-grounding-results">Streaming Grounding Results</a></li>
      <li><a href="#runtime-statistics">Runtime Statistics</a></li>
    </ul>
  </li>
  <li><a href="#miscellanea">Miscellanea</a>
    <ul>
      <li><a href="#simple-class-names">Simple Class Names</a></li>
      <li><a href="#new-weight-learning-method-gpp">New Weight Learning Method: GPP</a></li>
      <li><a href="#made-data-loading-errors-more-clear">Made Data Loading Errors More Clear</a></li>
      <li><a href="#removed-date-constanttype">Removed Date ConstantType</a></li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="infrastructure"><strong>Infrastructure</strong></h2>
<p>The 2.2.1 release comes with a few changes to the PSL development cycle and artifact deployment.</p>

<h3 id="artifacts-moved-to-maven-central">Artifacts Moved to Maven Central</h3>
<p>Starting with this release, PSL releases and artifacts will now be hosted through <a href="https://mvnrepository.com/repos/central">Maven Central</a>.
Maven Central is the default remote repository for Maven.
With PSL deployed there, there is no longer a need to use the old Maven repository at:
<a href="http://maven.linqs.org/maven/repositories/psl-releases/">http://maven.linqs.org/maven/repositories/psl-releases/</a>.
Old builds will continue to be hosted at the old repository for the foreseeable future.
To find the new builds you can go to the <a href="https://mvnrepository.com/artifact/org.linqs">org.linqs</a> group on Maven Central.
The development versions are labeled as <code class="language-plaintext highlighter-rouge">CANARY</code> releases.</p>

<p>Because PSL is now hosted on Maven central,
you can now remove the <a href="http://maven.linqs.org">maven.linqs.org</a> repository from your Maven configuration.
In most cases, this means that you can remove the following section from your <code class="language-plaintext highlighter-rouge">pom.xml</code> files:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;repositories&gt;</span>
    <span class="nt">&lt;repository&gt;</span>
        <span class="nt">&lt;releases&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
            <span class="nt">&lt;updatePolicy&gt;</span>daily<span class="nt">&lt;/updatePolicy&gt;</span>
            <span class="nt">&lt;checksumPolicy&gt;</span>fail<span class="nt">&lt;/checksumPolicy&gt;</span>
        <span class="nt">&lt;/releases&gt;</span>
        <span class="nt">&lt;id&gt;</span>psl-releases<span class="nt">&lt;/id&gt;</span>
        <span class="nt">&lt;name&gt;</span>PSL Releases<span class="nt">&lt;/name&gt;</span>
        <span class="nt">&lt;url&gt;</span>http://maven.linqs.org/maven/repositories/psl-releases/<span class="nt">&lt;/url&gt;</span>
        <span class="nt">&lt;layout&gt;</span>default<span class="nt">&lt;/layout&gt;</span>
    <span class="nt">&lt;/repository&gt;</span>
<span class="nt">&lt;/repositories&gt;</span>
</code></pre></div></div>

<h3 id="wiki-hosted-on-psllinqsorg">Wiki Hosted on psl.linqs.org</h3>
<p>The PSL Wiki <a href="https://github.com/linqs/psl/wiki">https://github.com/linqs/psl/wiki</a>
and PSL Development Wiki <a href="https://github.com/eriq-augustine/psl/wiki">https://github.com/eriq-augustine/psl/wiki</a>
have been moved to <a href="/wiki/">psl.linqs.org/wiki</a>.
All stable and development releases going forward will have a version of the wiki available as either
live webpages (for newer releases) or downloadable archives (for older releases).</p>

<h3 id="api-reference-hosted-on-psllinqsorg">API Reference Hosted on psl.linqs.org</h3>
<p>Along with the Wiki, the API reference will now also be hosted at <a href="/api/">psl.linqs.org/api</a>.
All stable and development releases going forward will have a version of the API reference available as either
live webpages (for newer releases) or downloadable archives (for older releases).</p>

<h3 id="development-repo-now-linqspsl">Development Repo Now linqs/psl</h3>
<p>PSL development has been moved from <a href="https://github.com/eriq-augustine/psl">eriq-augustine/psl</a>
to the canonical PSL repository: <a href="https://github.com/linqs/psl">linqs/psl</a>.
Any new pull requests should be submitted there.</p>

<h3 id="issues-moved-to-linqspsl">Issues Moved to linqs/psl</h3>
<p>Along with pull requests, issues have been moved to the canonical PSL repository: <a href="https://github.com/linqs/psl/issues">linqs/psl/issues</a>.
All old issues (along the with their comments and labels) have been migrated to this repository and any new issues should be submitted there.</p>

<h2 id="psl-interfaces"><strong>PSL Interfaces</strong></h2>
<p>The PSL 2.2.1 release comes with two new interfaces, and one deprecation.</p>

<h3 id="new-python-interface">New Python Interface</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/a38cffe5866f070f7db9665b5fa94f4f9d957a44"><em>a38cffe5</em></a></p>

<p>PSL 2.2.1 comes with the first official release of the PSL Python interface.
This package is called <code class="language-plaintext highlighter-rouge">pslpython</code> and is <a href="https://pypi.org/project/pslpython">available on PyPi</a>.
Therefore, it can be installed via pip:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install pslpython
</code></pre></div></div>
<p>The source for the interface is available in the <a href="https://github.com/linqs/psl/tree/2.2.1/psl-python">main PSL repository</a>.</p>

<p>Fully implemented examples can be found in the <a href="https://github.com/linqs/psl-examples">psl-examples repository</a>.
Below is a simplified example of the Python interface:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>

<span class="kn">from</span> <span class="nn">pslpython.model</span> <span class="kn">import</span> <span class="n">Model</span>
<span class="kn">from</span> <span class="nn">pslpython.partition</span> <span class="kn">import</span> <span class="n">Partition</span>
<span class="kn">from</span> <span class="nn">pslpython.predicate</span> <span class="kn">import</span> <span class="n">Predicate</span>
<span class="kn">from</span> <span class="nn">pslpython.rule</span> <span class="kn">import</span> <span class="n">Rule</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">Model</span><span class="p">(</span><span class="s">'sample-model'</span><span class="p">)</span>

<span class="c1"># Add predicates.
</span><span class="n">predicate</span> <span class="o">=</span> <span class="n">Predicate</span><span class="p">(</span><span class="s">'Foo'</span><span class="p">,</span> <span class="n">closed</span> <span class="o">=</span> <span class="bp">True</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">add_predicate</span><span class="p">(</span><span class="n">predicate</span><span class="p">)</span>

<span class="n">predicate</span> <span class="o">=</span> <span class="n">Predicate</span><span class="p">(</span><span class="s">'Bar'</span><span class="p">,</span> <span class="n">closed</span> <span class="o">=</span> <span class="bp">False</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">add_predicate</span><span class="p">(</span><span class="n">predicate</span><span class="p">)</span>

<span class="c1"># Add rules.
</span><span class="n">model</span><span class="p">.</span><span class="n">add_rule</span><span class="p">(</span><span class="n">Rule</span><span class="p">(</span><span class="s">'0.20: Foo(A, B) -&gt; Bar(A, B) ^2'</span><span class="p">))</span>
<span class="n">model</span><span class="p">.</span><span class="n">add_rule</span><span class="p">(</span><span class="n">Rule</span><span class="p">(</span><span class="s">'0.01: !Bar(A, B) ^2'</span><span class="p">))</span>

<span class="c1"># Load data.
</span><span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="s">'data'</span><span class="p">,</span> <span class="s">'foo_obs.txt'</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">get_predicate</span><span class="p">(</span><span class="s">'Foo'</span><span class="p">).</span><span class="n">add_data_file</span><span class="p">(</span><span class="n">Partition</span><span class="p">.</span><span class="n">OBSERVATIONS</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>

<span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="s">'data'</span><span class="p">,</span> <span class="s">'bar_targets.txt'</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">get_predicate</span><span class="p">(</span><span class="s">'Bar'</span><span class="p">).</span><span class="n">add_data_file</span><span class="p">(</span><span class="n">Partition</span><span class="p">.</span><span class="n">TARGETS</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>

<span class="c1"># Run inference.
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">infer</span><span class="p">()</span>

<span class="c1"># Write out results.
</span><span class="n">out_dir</span> <span class="o">=</span> <span class="s">'inferred-predicates'</span>
<span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">out_dir</span><span class="p">,</span> <span class="n">exist_ok</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>

<span class="n">out_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">out_dir</span><span class="p">,</span> <span class="s">"bar.txt"</span><span class="p">)</span>
<span class="n">results</span><span class="p">[</span><span class="n">model</span><span class="p">.</span><span class="n">get_predicate</span><span class="p">(</span><span class="s">'Bar'</span><span class="p">)].</span><span class="n">to_csv</span><span class="p">(</span><span class="n">out_path</span><span class="p">,</span> <span class="n">sep</span> <span class="o">=</span> <span class="s">"</span><span class="se">\t</span><span class="s">"</span><span class="p">,</span> <span class="n">header</span> <span class="o">=</span> <span class="bp">False</span><span class="p">,</span> <span class="n">index</span> <span class="o">=</span> <span class="bp">False</span><span class="p">)</span>
</code></pre></div></div>

<p>In addition to creating models in Python, you can use the PSL python package to invoke the PSL CLI interface directory.
Instead of invoking the PSL jar:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> psl.jar <span class="nt">--model</span> test.psl <span class="nt">--data</span> test.data
</code></pre></div></div>
<p>You can use the <code class="language-plaintext highlighter-rouge">pslpython</code> package already installed via pip:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> pslpython.cli <span class="nt">--model</span> test.psl <span class="nt">--data</span> test.data
</code></pre></div></div>

<p>Additionally, any arguments supported by the CLI interface can be passed to <code class="language-plaintext highlighter-rouge">pslpython.cli</code> as well:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> pslpython.cli <span class="nt">--model</span> test.psl <span class="nt">--data</span> test.data <span class="nt">--postgres</span> myDB <span class="nt">-D</span> log4j.threshold<span class="o">=</span>DEBUG
</code></pre></div></div>

<h3 id="new-java-interface">New Java Interface</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/7e305dfea1a0e092b43ece80f4e44f71777ee330"><em>7e305dfe</em></a></p>

<p>PSL 2.2.1 comes with a new Java interface.
This interface works much like the Groovy interface with some slight differences.
Fully implemented examples of the Java interface can be found in the <a href="https://github.com/linqs/psl-examples">psl-examples repository</a>.</p>

<p>Instead of using <code class="language-plaintext highlighter-rouge">org.linqs.psl.groovy.PSLModel</code>, <code class="language-plaintext highlighter-rouge">org.linqs.psl.java.PSLModel</code> is used.
The methods for the PSLModel class are now explicitly named, instead of being overloads of the same <code class="language-plaintext highlighter-rouge">add()</code> method.
For example, instead of <code class="language-plaintext highlighter-rouge">model.add predicate: "Foo", ...</code>, you will use <code class="language-plaintext highlighter-rouge">model.addPredicate("Foo", ...)</code>.
The full API for the PSLModel class can be found <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/java/PSLModel.html">here</a>.</p>

<p>The Groovy interface allows rules to be specified as part of the Groovy syntax.
However, rules in the Java interface must be specified as a String.</p>

<p>To access predicates in the Java interface, you can no longer just reference them by name with no context.
Now, you can ask the model for a predicate by name.
In Groovy:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Inserter inserter = dataStore.getInserter(Foo, obsPartition);
</code></pre></div></div>
<p>In Java:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Inserter inserter = dataStore.getInserter(model.getStandardPredicate("Foo"), obsPartition);
</code></pre></div></div>

<h3 id="groovy-interface-deprecated">Groovy Interface Deprecated</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/476dfd1e745919dc5a2cef4ac521806eb252cac1"><em>476dfd1e</em></a></p>

<p>With the addition of the <a href="#new-java-interface">new Java interface</a>, the Groovy interface has officially been deprecated.
It will be removed from the next release of PSL.
Dropping support for Groovy will allow us to support a wider range of Java versions (instead of just 7 and 8).</p>

<h2 id="cli-improvements"><strong>CLI Improvements</strong></h2>

<h3 id="functional-predicates">Functional Predicates</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/ac2f9a30d42951a4c0c9a90ade1edfdce0e2fe5d"><em>ac2f9a30</em></a></p>

<p><a href="/wiki/2.2.1/External-Functions.html">Functional predicates</a> are now supported in the CLI.
To use these, a <code class="language-plaintext highlighter-rouge">function</code> key needs to be specified in the predicate definition, e.g:</p>
<pre><code class="language-yaml:">  Knows/2: open
  Likes/2: closed
  Lived/2: closed
  SimName:
    - function: org.foo.bar.SimNameExternalFunction
</code></pre>

<p>In this case, the functional predicate is <code class="language-plaintext highlighter-rouge">SimName</code> and it is implemented by the <code class="language-plaintext highlighter-rouge">SimNameExternalFunction</code> class.
<code class="language-plaintext highlighter-rouge">SimName</code> can then be used in a rule like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1.0: SimName(P1, P2) &amp; Lived(P1, L) &amp; Lived(P2, L) &amp; (P1 != P2) -&gt; Knows(P1, P2) ^2
</code></pre></div></div>

<h3 id="multiple-evaluators">Multiple Evaluators</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/d63a8f7ee1c9bfb881bb53770e3e84db34c2668b"><em>d63a8f7e</em></a></p>

<p>The CLI can now use multiple evaluators in one run.
This can be done by passing by multiple evaluators to the <code class="language-plaintext highlighter-rouge">--eval</code> argument:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java -jar psl.jar --model test.psl --data test.data --eval DiscreteEvaluator ContinuousEvaluator
</code></pre></div></div>

<h3 id="output-ground-rules">Output Ground Rules</h3>
<p>Commits: <a href="https://github.com/linqs/psl/commit/62fbd2cc20c8360c365a02be2cdc4f871a4f5798"><em>62fbd2cc</em></a> ,
<a href="https://github.com/linqs/psl/commit/b901e937ed2869b5dcc184585ea29c5fa77b1170"><em>b901e937</em></a></p>

<p>The CLI accepts two new arguments that can be used to see the ground rules being processed.<br />
<code class="language-plaintext highlighter-rouge">-gr</code>/<code class="language-plaintext highlighter-rouge">--groundrules</code> can be used to output the ground rules <strong>before</strong> inference is run.
This will show the ground rules as early as possible.
While <code class="language-plaintext highlighter-rouge">--satisfaction</code> will output ground rules along with their satisfaction value <strong>after</strong> inference is run.</p>

<p>If you are concerned about an issue with your rules/data and want to see the ground rules created,
then <code class="language-plaintext highlighter-rouge">--groundrules</code> is the option you should use.
If you are curious about the value that different rules are taking,
then <code class="language-plaintext highlighter-rouge">--satisfaction</code> is the option you should use.</p>

<p>Either option can be specified without arguments, and the results will be output to stdout.
You can also specify an optional path with either argument and the results will be output there.</p>

<h3 id="skip-database-commit">Skip Database Commit</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/3ced4b20be80fc17403663fe16e194bcfcaf6a3a"><em>3ced4b20</em></a></p>

<p>If you do not need the results of inference saved into the database,
then you can save time by skipping the writing of results to the database using the <code class="language-plaintext highlighter-rouge">--skipAtomCommit</code> argument.</p>

<h3 id="remove-extra-quoting">Remove Extra Quoting</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/cbe7fd8a3c8e5dc9a296836f5574e98353328323"><em>cbe7fd8a</em></a></p>

<p>Constants are no longer quoted in the inferred predicate output produced by the CLI.
This may break existing scripts that parse this output,
but now files output by the CLI will match the format consumed by the CLI (by default).</p>

<h3 id="runsh-takes-arguments">run.sh Takes Arguments</h3>

<p>The <code class="language-plaintext highlighter-rouge">run.sh</code> scripts in CLI implementations for <code class="language-plaintext highlighter-rouge">psl-examples</code> now takes arguments that are passed directly to the CLI.
Specifying these arguments is equivalent to adding these arguments to the <code class="language-plaintext highlighter-rouge">ADDITIONAL_PSL_OPTIONS</code> constant.
For example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./run.sh -D log4j.threshold=DEBUG --postgres psl
</code></pre></div></div>

<h2 id="performance"><strong>Performance</strong></h2>

<h3 id="reduced-memory">Reduced Memory</h3>

<p>A lot of effort was put into reducing the memory burden of PSL for the 2.2.1 release.
Both in terms of allocations and total persisted memory.
We have observed the total memory consumption in PSL drop between 17.5% and 45.7% (depending on the exact model and data).
Below you can see an example of the same model and data in PSL 2.1.0 (left) vs PSL 2.2.1 (right).
The blue portion of the graph is the actual memory being used.</p>

<p><img src="/assets/images/2.2.1/psl-memory-improvements-2.2.1-opt.png" alt="" class="center-image" /></p>

<h3 id="smaller-types">Smaller Types</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/9a34ce235b65cd549c28b9b64cbc94a496d1350c"><em>9a34ce23</em></a></p>

<p>Where possible, standard types have been replaced by their shorter sibling
(<code class="language-plaintext highlighter-rouge">int</code> replaced with <code class="language-plaintext highlighter-rouge">short</code>, <code class="language-plaintext highlighter-rouge">double</code> replaced with <code class="language-plaintext highlighter-rouge">float</code>, etc).
This allows us to trade unused precision for memory and speed (depending on the system architecture).</p>

<h3 id="matrix-operations">Matrix Operations</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/8f034fa8315d3f6461cd38435612d889c745f4a2"><em>8f034fa8</em></a></p>

<p>We have added the <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/util/FloatMatrix.html"><code class="language-plaintext highlighter-rouge">FloatMatrix</code></a> class to handle low-level matrix operations.
This classes uses the <a href="https://www.netlib.org/">Netlib library</a> to call into the low-level <a href="https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms">BLAS</a> and <a href="https://en.wikipedia.org/wiki/LAPACK">LAPACK</a> libraries.
This allows us to easily perform efficient matrix operations.</p>

<h3 id="streaming-grounding-results">Streaming Grounding Results</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/8f4de8464b79ace097ad577dff0bcbfa63369b3e"><em>8f4de846</em></a></p>

<p>Grounding results can now be streamed from the database using the <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/database/QueryResultIterable.html"><code class="language-plaintext highlighter-rouge">QueryResultIterable</code></a> class.
This allows the user to iterate through the grounding results without needing to keep them all in memory at the same time.</p>

<h3 id="runtime-statistics">Runtime Statistics</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/df58a390181a1f32ef536674e56e92d20d2257e0"><em>df58a390</em></a></p>

<p>A new class, <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/util/RuntimeStats.html">RuntimeStats</a>,
has been introduced to keep track of JVM statistics throughout the lifetime of a PSL program.
Setting the configuration option <code class="language-plaintext highlighter-rouge">runtimestats.collect</code> to <code class="language-plaintext highlighter-rouge">true</code> will enable the statistics collection.
These collected stats are currently output to the <code class="language-plaintext highlighter-rouge">INFO</code> log level when the JVM terminates.</p>

<p>Currently, memory information is automatically collected.
In addition, the user can call the static <code class="language-plaintext highlighter-rouge">logDiskRead()</code> and <code class="language-plaintext highlighter-rouge">logDiskWrite()</code> methods to keep track of I/O operations.</p>

<p>Using the statistics looks like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>linqs@comp:~/code/psl-examples/simple-acquaintances/cli$ ./run.sh -D runtimestats.collect=true
Running PSL Inference
0    [main] INFO  org.linqs.psl.cli.Launcher  - Running PSL CLI Version 2.2.1-a573763
... &lt; Omitted in the changelog for brevity &gt; ...
308 [main] INFO  org.linqs.psl.application.inference.InferenceApplication  - Inference complete.
1308 [main] INFO  org.linqs.psl.application.inference.InferenceApplication  - Writing results to Database.
1340 [main] INFO  org.linqs.psl.application.inference.InferenceApplication  - Results committed to database.
1340 [main] INFO  org.linqs.psl.cli.Launcher  - Inference Complete
1349 [main] INFO  org.linqs.psl.cli.Launcher  - Starting evaluation with class: org.linqs.psl.evaluation.statistics.DiscreteEvaluator.
1368 [main] INFO  org.linqs.psl.cli.Launcher  - Evaluation results for KNOWS -- Accuracy: 0.915254, F1: 0.933333, Positive Class Precision: 0.945946, Positive Class Recall: 0.921053, Negative Class Precision: 0.863636, Negative Class Recall: 0.904762
1368 [main] INFO  org.linqs.psl.cli.Launcher  - Evaluation complete.
1377 [Thread-1] INFO  org.linqs.psl.util.RuntimeStats  - Total Memory (bytes) -- Min:    504889344, Max:    504889344, Mean:    504889344, Count:            6
1377 [Thread-1] INFO  org.linqs.psl.util.RuntimeStats  - Free Memory (bytes)  -- Min:    403775464, Max:    494319512, Mean:    437039418, Count:            6
1377 [Thread-1] INFO  org.linqs.psl.util.RuntimeStats  - Used Memory (bytes)  -- Min:     10569832, Max:    101113880, Mean:     67849925, Count:            6
1377 [Thread-1] INFO  org.linqs.psl.util.RuntimeStats  - Max Memory (bytes)   -- Min:   7475298304, Max:   7475298304, Mean:   7475298304, Count:            6
1378 [Thread-1] INFO  org.linqs.psl.util.RuntimeStats  - IO Reads (bytes)     -- Min:            0, Max:            0, Mean:            0, Count:            0, Total:            0
1378 [Thread-1] INFO  org.linqs.psl.util.RuntimeStats  - IO Writes (bytes)    -- Min:            0, Max:            0, Mean:            0, Count:            0, Total:            0
linqs@comp:~/code/psl-examples/simple-acquaintances/cli$
</code></pre></div></div>

<h2 id="miscellanea"><strong>Miscellanea</strong></h2>

<h3 id="simple-class-names">Simple Class Names</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/00b603211d070729bc486c558846c3cfebcc4bb7"><em>00b60321</em></a></p>

<p>In any case where a classname is used as a configuration option or argument,
you can now specify the classes shortname instead of its fully-qualified name.
For example, instead of:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java -jar psl.jar --model test.psl --data test.data --eval org.linqs.psl.evaluation.statistics.DiscreteEvaluator
</code></pre></div></div>
<p>You can do:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java -jar psl.jar --model test.psl --data test.data --eval DiscreteEvaluator
</code></pre></div></div>

<h3 id="new-weight-learning-method-gpp">New Weight Learning Method: GPP</h3>

<p>With the release of PSL 2.2.1, we are adding a new weight learning method called Gaussian Process Prior (GPP), which is based on Bayesian optimization.</p>

<p>GPP is a search-based weight learning method that works by approximating a user defined metric and evaluating the PSL model on a number of strategically chosen weights.
Although GPP tends to work better with a smoother metric, it can work well with any metric.
The best metric to use depends on the specific problem being modeled.
A major benefit of GPP, and all search-based methods, is that the metric being optimized can be the same as the metric that results are evaluated on.
In contrast, likelihood-based methods (e.g. <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/application/learning/weight/maxlikelihood/MaxLikelihoodMPE.html">Maximum Likelihood MPE</a>) maximize the likelihood,
which may not be strongly correlated with the desired evaluation metrics.
For further details about GPP, please see the paper: <a href="https://linqs.soe.ucsc.edu/node/355">BOWL Bayesian Optimization for Weight Learning in Probabilistic Soft Logic</a>.</p>

<p>To use GPP in PSL, you need to choose the right weight learning class and an evaluator that GPP will use to evaluate weight configurations.
The class for GPP is <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/application/learning/weight/bayesian/GaussianProcessPrior.html"><code class="language-plaintext highlighter-rouge">org.linqs.psl.application.learning.weight.bayesian.GaussianProcessPrior</code></a>.
You can choose an evaulator by setting the <code class="language-plaintext highlighter-rouge">weightlearning.evaluator</code> configuration option to any <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/evaluation/statistics/Evaluator.html">Evaluator</a>.</p>

<p>For example, you can use the following command to use GPP in the CLI:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./run.sh --learn GaussianProcessPrior -D weightlearning.evaluator=DiscreteEvaluator
</code></pre></div></div>

<p>GPP has four main configuration options that the user should be aware of:</p>
<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">weightlearning.evaluator</code><br />
Domain: Any <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/evaluation/statistics/Evaluator.html">Evaluator</a><br />
Default: <a href="https://javadoc.io/static/org.linqs/psl-core/2.3.0/org/linqs/psl/evaluation/statistics/ContinuousEvaluator.html">ContinuousEvaluator</a><br />
The user defined metric function that GPP uses to evaluate and optimize weights.
The best evaluator to use depends on your specific problem.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">gpp.maxiterations</code><br />
Domain: Integer in (0, ∞)<br />
Default: 25<br />
The maximum number of times that BOWL will conduct evaluations before choosing the best set of weights.
100 iterations is typically enough for even difficult domains.
Keep in mind that the time taken to perform full learning in BOWL grows quadratically with the number of iterations.
Therefore, if you choose a large number (such as 500), learning might take days to finish.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">gpp.explore</code><br />
Domain: Float in (0.0, ∞)<br />
Default: 2.0<br />
This determines how the weights will be chosen for evaluation.
A lower value implies that the weights chosen for evaluation will be clustered around one region whereas a higher value will lead to exploration of weights that are as distinct as possible.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">gppker.reldep</code><br />
Domain: Float in (0.0, ∞)<br />
Default: 1.0<br />
The relative dependence value given in GPP.
The exploration space increases as the number of rules in the model increase.
A smaller value would imply that weights are very distinct and related, hence requiring fewer iterations.</p>
  </li>
</ul>

<h3 id="made-data-loading-errors-more-clear">Made Data Loading Errors More Clear</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/1e889f49d23ebc438e581fec8a092e50526d9550"><em>1e889f49</em></a></p>

<p>File paths were added to several data loading errors to make them more clear.</p>

<h3 id="removed-date-constanttype">Removed Date ConstantType</h3>
<p>Commit: <a href="https://github.com/linqs/psl/commit/df5e1b641d5274dd590bc2a917259a9e38bed464"><em>df5e1b64</em></a></p>

<p>The <code class="language-plaintext highlighter-rouge">ConstantType.Date</code> type have been removed as predicate argument type.
Instead, users should use <code class="language-plaintext highlighter-rouge">ConstantType.String</code> with the date represented as a string (we suggest <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>),
or users should use <code class="language-plaintext highlighter-rouge">ConstantType.Integer</code> and represent the date as an <a href="https://en.wikipedia.org/wiki/Unix_time">Unix/Epoch time</a>.</p>]]></content><author><name>Eriq Augustine &amp; Vihang Godbole</name></author><category term="release" /><category term="v2.2.1" /><summary type="html"><![CDATA[We are happy to announce the release of PSL version 2.2.1! We have made great improvements to PSL in the areas of usability and performance. In this changelog, you will find a list of the major changes in 2.2.1 as well as information on migrating from 2.1.0.]]></summary></entry><entry><title type="html">Joint Models of Disagreement and Stance</title><link href="/blog/2018/07/31/stance.html" rel="alternate" type="text/html" title="Joint Models of Disagreement and Stance" /><published>2018-07-31T17:00:00+00:00</published><updated>2018-07-31T17:00:00+00:00</updated><id>/blog/2018/07/31/stance</id><content type="html" xml:base="/blog/2018/07/31/stance.html"><![CDATA[<p>This post accompanies the PSL model and corpora described in the <a href="https://linqs.soe.ucsc.edu/node/258">“Joint Models of Disagreement and Stance” ACL paper</a>.</p>

<p><img src="/assets/images/stance/fig1.png" alt="Figure 1" class="center-image" /></p>

<p>In this PSL inference task, we’ll work with text from online debate forums.
An online debate forum, e.g. <a href="createdebate.org">createdebate.org</a>, features debates on several socio-political issues, or topics.
For example, one topic might be whether or not the government should increase spending, which we’ll succinctly call “spending.”
A user starts a debate by writing a post in which she indicates her stance towards the topic.
For simplicity throughout, we’ll consider pro and anti as the stances.
Let’s suppose she’s pro-spending.
Another user replies to this user to argue his opposing point, and his post will contain language indicating his anti-spending view and textual cues that he disagrees with our original user.</p>

<p>We can view these exchanges on an online debate site as a complex graph with vertices and edges.
We have both user and post vertices, connected by a type of directed edge that tells us which user wrote which post.
Of course, the post is associated with its text, which we can describe as an array of unigrams appearing in that post.
We also have directed edges connecting users when one user replies to another.</p>

<p>When we want to build machine learning models, we also associate random variables of interest to either these nodes (users, posts) or edges (replies) whose values we want to infer.
In this example, we will jointly predict the stances of all users and whether or not replying users disagree or agree on stance.
Each user will have a binary pro/anti stance variable which we will build a model to infer.
Each reply edge will also be associated with a binary same/opposite stance variable which our model will predict together with stance.</p>

<p>Let’s see the step-by-step approach to building this PSL model using predicates and rules:</p>

<p>First, we’ll specify predicates for our target random variables – stance and disagreement.
We represent stance of a user with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>isPro(User, Topic)
</code></pre></div></div>

<p>Where variable <code class="language-plaintext highlighter-rouge">U</code> will be substituted all the users in our dataset and variable <code class="language-plaintext highlighter-rouge">T</code> will substituted with all topics in our domain.
As an example, if we had just one topic, “spending” and two users, “alice” and “bob”, we will get the following ‘ground atoms’ of <code class="language-plaintext highlighter-rouge">isPro(User, Topic)</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>isPro("alice", "spending")
isPro("bob", "spending")
</code></pre></div></div>

<p>Remember that we want to predict the values for these atoms and in PSL, we infer values between 0 and 1.
For example, if “alice” uses pro-spending language and “bob” uses anti-spending language, we’d like to infer values like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>isPro("alice", "spending") = 0.9
isPro("bob", "spending") = 0.1
</code></pre></div></div>

<p>Next, we need a way of using the language in users’ posts as a signal to our model.
In this problem, we train a logistic regression classifier outside of PSL whose features are counts of unigrams from all of a users’ posts and whose training data are pro/anti labels for all users.
However, this classifier uses only information “local” to each user and can easily make mistakes since language is ambiguous.
We encode this signal with the predicate:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>localPro(User, Topic)
</code></pre></div></div>

<p>We observe the class probabilities from our logistic regression classifier as its values.
For example, our noisy local classifier might output, for “alice” and “bob”</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>localPro("alice", "spending") = 0.4
localPro("bob", "spending") = 0.6
</code></pre></div></div>

<p>To use PSL to smoothen these errors, let’s make use of our insight that the text also signals same/opposite stance notions of disagreement between users who reply to one another.
Exactly like stance, we can specify a disagreement random variable to infer:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>disagrees(User1, User2)
</code></pre></div></div>

<p>Where <code class="language-plaintext highlighter-rouge">User1</code> replies to <code class="language-plaintext highlighter-rouge">User2</code> in a thread, and our training label for this prediction is 1 if <code class="language-plaintext highlighter-rouge">User1</code> and <code class="language-plaintext highlighter-rouge">User2</code> have the same stance and 0 otherwise.
We similarly train a local logistic regression classifier based on unigrams and encode that signal similarly to stance:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>localDisagree(User1, User2)
</code></pre></div></div>

<p>Suppose that for “alice” and “bob”, we have a stronger disagreement signal based on the words “bob” uses to debate with “alice”.
We might observe the following local prediction for disagreement:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>localDisagree("alice", "bob") = 0.8
</code></pre></div></div>

<p>This information should help us make better stance inferences!
How can we model these two prediction problems with PSL rules?</p>

<p>Let’s start by using our local logistic regression text classifiers to inform our final stance and disagreement predictions with the rules:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>localPro(U, T) &gt;&gt; isProAuth(U, T)
~localPro(U, T) &gt;&gt; ~isProAuth(U, T)
localDisagrees(U1, U2) &gt;&gt; disagrees(U1, U2)
~localDisagrees(U1, U2) &gt;&gt; ~disagrees(U1, U2)
</code></pre></div></div>

<p>We see that rules such as the second rule allow us to also reason about when a user’s stance is not pro by using the class probability of anti from our logistic regression classifier.
Now, let’s see how disagreement can help us predict users’ stances:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>disagrees(U1, U2) &amp; responds(U1, U2) &amp; participates(U2, T) &amp;
   isProAuth(U1, T) &gt;&gt; ~isProAuth(U2, T)
disagrees(U1, U2) &amp; responds(U1, U2) &amp; participates(U1, T) &amp;
   participates(U2, T) &amp; ~isProAuth(U1, T) &gt;&gt; isProAuth(U2, T)
</code></pre></div></div>

<p>We don’t need to worry about auxiliary predicates like responds and participates.
They simply give us information about who replied to whom and whether users participate in a certain topic T.
When PSL grounds these rules, these predicates help define the population of constants that should be involved in the ground rules.</p>

<p>For ease, let’s consider the logic that’s doing the work in this rule:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>disagrees(U1, U2) &amp; isProAuth(U1, T) &gt;&gt; ~isProAuth(U2, T)
disagrees(U1, U2) &amp; ~isProAuth(U1, T) &gt;&gt; isProAuth(U2, T)
</code></pre></div></div>

<p>These two rules encode our intuition that if two users likely disagree, they should have opposite stances. We have two rules to encode both combinations of possible stance assignments to the users.
Importantly, these rules help us make collective inferences, where the predicted stance of one user depends on that of a neighboring user in the debate graph.
We can easily detect collective rules by recognizing that a target predicate is in both the body and head of the rule.</p>

<p>Now, we’ll want to encode a similar intuition for agreement: if users probably agree, they should the same stance.
Again, for ease, let’s see the core logic that does all the inference legwork:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~disagrees(U1, U2) &amp; ~isProAuth(U1, T) &gt;&gt; ~isProAuth(U2, T)
~disagrees(U1, U2) &amp; isProAuth(U1, T) &gt;&gt; isProAuth(U2, T)
</code></pre></div></div>

<p>But we want to similarly use stance variables to inform disagreement and agreement.
We can encode the same intuitions as above with the following logic:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>isProAuth(U1, T) &amp; ~isProAuth(U2, T) &gt;&gt; disagrees(U1, U2)
~isProAuth(U1, T) &amp; isProAuth(U2, T) &gt;&gt; disagrees(U1, U2)

~isProAuth(U1, T) &amp; ~isProAuth(U2, T) &gt;&gt; ~disagrees(U1, U2)
isProAuth(U1, T) &amp; isProAuth(U2, T) &gt;&gt; ~disagrees(U1, U2)
</code></pre></div></div>

<p>Notice that in these rules, stance variables are in the body of the rule and disagreement is in the head.
Let’s turn back our example where we observed:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>localPro("alice", "spending") = 0.4
localPro("bob", "spending") = 0.6
localDisagree("alice", "bob") = 0.8
</code></pre></div></div>

<p>With the rules above, we can expect to see that the final inferences for <code class="language-plaintext highlighter-rouge">isPro("alice", "spending")</code> and <code class="language-plaintext highlighter-rouge">isPro("bob", "spending")</code> will be sided correctly unlike the local stance predictions because the local disagreement signal combined with the collective rules will propagate correct information.</p>

<p>Now that we’ve looked at the predicates and rules of our joint disagreement and stance PSL model, let’s run inference on a real debate topic and set of discussions.</p>

<p>Download the code and data by cloning this git repository: <a href="https://bitbucket.org/linqs/psl-joint-stance.git">https://bitbucket.org/linqs/psl-joint-stance.git</a>.</p>

<p>Let’s go to psl-forums directory.
Before running a simple example, navigate to <code class="language-plaintext highlighter-rouge">data/simple_abortion/0/train</code> and familiarize yourself with the input files for the predicates described above.
You’ll see a corresponding .csv file for each predicate name.</p>

<p>Remember that while we observe values for: <code class="language-plaintext highlighter-rouge">localPro</code>, <code class="language-plaintext highlighter-rouge">localDisagree</code>, <code class="language-plaintext highlighter-rouge">participates</code>, <code class="language-plaintext highlighter-rouge">responds</code>.</p>

<p>The files for disagrees and isProAuth tell us who the users (or authors) are that we have training labels for (or in the case of the test set, ground truth labels) and gives us training labels so that we learn weights for each rule.</p>

<p>Navigate back to the psl-forums directory and use <code class="language-plaintext highlighter-rouge">./run_simple.sh</code> to run cross validation on a single forum topic.
There are 5 train/test splits and the code learns weights using the stance and disagreement labels in train and evaluates the inferred values for targets in the test directory.
When complete, the output should show:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[main] WARN  edu.ucsc.cs.JointStanceDisagreementPrediction  - Accuracy: 0.7203389830508474
[main] WARN  edu.ucsc.cs.JointStanceDisagreementPrediction  - Accuracy: 0.6233766233766234
[main] WARN  edu.ucsc.cs.JointStanceDisagreementPrediction  - Accuracy: 0.676056338028169
[main] WARN  edu.ucsc.cs.JointStanceDisagreementPrediction  - Accuracy: 0.6466165413533834
[main] WARN  edu.ucsc.cs.JointStanceDisagreementPrediction  - Accuracy: 0.6509433962264151
</code></pre></div></div>

<p>This gives us the accuracy of prediction on the test set for each random split.
You can experiment by commenting out combinations of rules and evaluating the accuracy.</p>

<p>You can also run the full set of cross-validation experiments described in our paper.
To do this, first run <code class="language-plaintext highlighter-rouge">./fetch_data.sh</code> in the <code class="language-plaintext highlighter-rouge">data</code> directory.
Then from the psl-forums directory, call <code class="language-plaintext highlighter-rouge">./run_full_experiment.sh</code>.</p>

<hr />

<p><strong>Paper</strong>: <a href="https://linqs.soe.ucsc.edu/node/258">Dhanya Sridhar, James Foulds, Bert Huang, Marilyn Walker, Lise Getoor. Joint Models Of Disagreement And Stance In Online Debate. Association for Computational Linguistics (ACL), 2015</a></p>

<p><strong>Code</strong>: <a href="https://bitbucket.org/linqs/psl-joint-stance.git">https://bitbucket.org/linqs/psl-joint-stance.git</a></p>]]></content><author><name>Dhanya Sridhar</name></author><category term="application" /><summary type="html"><![CDATA[This post accompanies the PSL model and corpora described in the “Joint Models of Disagreement and Stance” ACL paper.]]></summary></entry><entry><title type="html">Hybrid Recommender Systems Using PSL</title><link href="/blog/2018/07/30/hyper.html" rel="alternate" type="text/html" title="Hybrid Recommender Systems Using PSL" /><published>2018-07-30T17:00:00+00:00</published><updated>2018-07-30T17:00:00+00:00</updated><id>/blog/2018/07/30/hyper</id><content type="html" xml:base="/blog/2018/07/30/hyper.html"><![CDATA[<p>In traditional recommender systems, recommendations are usually made by using the information provided by a rating matrix.
However, new recommender systems have access to much richer information.
For example, in a movie recommender system information about the content of the movies might be available, like actors, directors, plot, etc.
Additionally, there might be social information available for the users, e.g., a user might login to the recommender system through his Facebook account.
More than that, demographic information about the user might be available, such as age or gender.
In this post we will show that all this rich information can be used to improve recommendations by using PSL.</p>

<p>The related work has shown that combining rating information with other data sources improves recommendation performance.
Early work on the field combines ratings with information about the content of the items and improves performance.
This is easily explainable: if a user likes one horror movie, it’s probable that she will like other horror movies as well.
Moreover, as social media became popular, relationship information is also found to be useful in the recommender system setting.
This is easily explainable again: homophily states that we are connected with people who are similar to us, so we may share the same tastes with our friends.</p>

<p><img src="/assets/images/hyper/fig1.png" alt="Figure 1" class="center-image" /></p>

<p>Finally, ensemble methods, which combine the predictions of multiple recommender algorithms, are also known to improve performance.
This was one of the biggest lessons learned from the Netflix competition, that “predictive accuracy is substantially improved when blending multiple predictors” [Bell et al.].</p>

<p>Related work has proposed hybrid recommender systems that have been shown to work better than non-hybrid systems.
However, these systems typically fall short on at least one of our desirata: generality, extensibility, or scalability.
Most of the work so far combine collaborative filtering with only simple content features, or just one other data modality.
On the other hand, probabilistic graphical modeling approaches proposed for recommendations are typically more general than other hybrid models.
However, they suffer in terms of scalability when it comes to practical real-world recommendation tasks.
These models are expected to be computationally challenging in the general case, as Inference is NP-hard.</p>

<p>To address this challenge, we propose to use PSL to build a hybrid framework which we call HyPER: the hybrid probabilistic extensible recommender.
Hyper is a general, extensible, and scalable recommender framework.</p>

<p>To build HyPER, we view the problem as a bipartite graph where users and items are nodes (Figure 2).
Edges illustrate ratings of users over items.
The weights on the edges represent the value of the rating. Dashed edges represent unobserved ratings the values of which we wish to predict.</p>

<p class="center-text"><img src="/assets/images/hyper/fig2.png" alt="" class="center-image" />
<em>Figure 2: Recommendation graph when only ratings are available</em></p>

<p>We build the hybrid model by adding edges to this graph.
Additional edges represent the additional information provided by different sources.
For example we can represent social relationships between users by adding edges between users.
Figure 3 presents the extended recommendation graph that we produce to encode all different information sources and algorithms.
More specifically, we encode user similarities that come from the user-based neighborhood methods and social information by adding edges between users.
We encode item similarities that come from the item-based neighborhood methods and content information by adding between items.
We finally encode predictions from other algorithms by adding edges between users and items.
The missing ratings are predicted by reasoning over the enhanced graph by performing inference in a graphical model.</p>

<p class="center-text"><img src="/assets/images/hyper/fig3.png" alt="" class="center-image" />
<em>Figure 3: Recommendation graph when additional information (e.g., user and item similarities) are available.</em></p>

<p>After defining the recommendation graph, we need to reason over it to make predictions.
As initially discussed, we use PSL because the formalism of this model allows exact, efficient, and scalable inference.
In particular, we use first order PSL logical rules to define our hybrid recommender model.
First, we introduce rules from item-based neighborhood methods.
The intuition is that similar items get similar ratings by a given user.</p>

<p><img src="/assets/images/hyper/fig4.png" alt="" class="center-image" /></p>

<p>The rule states that IF items i1 and i2 are similar, for a given similarity measure, AND user u rated item i1 highly, THEN user u will rate item i2 highly.
The predicate Rating(u, i) takes a value in the interval [0, 1] and represents the normalized value of the rating that a user u gave to an item i, while SimilarUserssim(u1, u2) is binary, with value 1 iff u1 is one of the k-nearest neighbors of u2.
We note that we can use as many similarity metrics as we wish.
Popular options include cosine, adjusted cosine, and Pearson correlation.</p>

<p>Working in a similar way we can introduce rules from the user-based neighborhood methods.</p>

<p><img src="/assets/images/hyper/fig5.png" alt="" class="center-image" /></p>

<p>Intuitively, the rule states that IF users u1 and u2 are similar AND u1 rated i highly, then it is likely that u2 rated i highly as well.
Like before, the predicate SimilarItemssim(i1, i2) is binary, with value 1 iff i1 is one of the k-nearest neighbors of i2 (using similarity measure SIM), while Rating(u, i) represents the normalized value of the rating of user u to item i, as discussed above.
Again, we can use multiple similarity measures used in the literature for user similarities.</p>

<p>We continue by defining mean centering corrections that are usually used in the neighborhood-based methods.
These corrections capture the fact that some items are very popular and that is why they get high ratings.
Or they capture the fact that a user is strict so he rarely gives high ratings or the opposite.
We introduce such corrections through the use of prior rules in our model.</p>

<p><img src="/assets/images/hyper/fig6.png" alt="" class="center-image" /></p>

<p>The first two rules state that the rating a user gives to an item should be close to the average rating of the user.
The predicate AverageUserRating(u) represents the average of the ratings over the set of items that user u provided in the training set.
The last two rules state the similar for the item case.
Similarly, AverageUserRating(i) represents the average of the user ratings an item i has received.
Each pair of PSL rules per-user and per-item corresponds to a “V-shaped” function centered at the average rating, which penalizes the predicted rating for being different in either direction from this average.
These rules are also useful for the cold-start setting, where we might have no prior knowledge for a user or an item.</p>

<p>Additionally, we can incorporate rules that capture the relationships. Two users that are friends tend to give similar ratings.</p>

<p><img src="/assets/images/hyper/fig7.png" alt="" class="center-image" /></p>

<p>Finally as we stated, we can use the predictions from other algorithms.
We can do that by introducing rules in the form of priors, like the one shown below.
We note that we can use as many different algorithms as we wish.</p>

<p><img src="/assets/images/hyper/fig8.png" alt="" class="center-image" /></p>

<p>Finally, we note that HyPER is extensible beyond the rules and data types that I have shown here.
If a new data source becomes available, the only thing that we need to do is to add another first-order PSL rule, and then retrain the model.</p>

<hr />

<p><strong>Paper</strong>: <a href="https://linqs.soe.ucsc.edu/node/257">P. Kouki, S. Fakhrei, J. Foulds, M. Eirinaki, L. Getoor. HyPER: A Flexible and Extensible Probabilistic Framework for Hybrid Recommender Systems. RecSys, 2015</a></p>

<p><strong>Code + Data</strong>: <a href="https://github.com/pkouki/recsys2015">https://github.com/pkouki/recsys2015</a></p>

<p><strong>References</strong>:
[Bell et al.] R. Bell, Y. Koren, C. Volinsky, The BellKor Solution to the Netflix Prize, 2007</p>]]></content><author><name>Pigi Kouki</name></author><category term="application" /><summary type="html"><![CDATA[In traditional recommender systems, recommendations are usually made by using the information provided by a rating matrix. However, new recommender systems have access to much richer information. For example, in a movie recommender system information about the content of the movies might be available, like actors, directors, plot, etc. Additionally, there might be social information available for the users, e.g., a user might login to the recommender system through his Facebook account. More than that, demographic information about the user might be available, such as age or gender. In this post we will show that all this rich information can be used to improve recommendations by using PSL.]]></summary></entry><entry><title type="html">PSL 2.1.0 Release</title><link href="/blog/2018/07/29/psl-2.1.0-release.html" rel="alternate" type="text/html" title="PSL 2.1.0 Release" /><published>2018-07-29T17:00:00+00:00</published><updated>2018-07-29T17:00:00+00:00</updated><id>/blog/2018/07/29/psl-2.1.0-release</id><content type="html" xml:base="/blog/2018/07/29/psl-2.1.0-release.html"><![CDATA[<p>We are happy to announce the release of PSL version <code class="language-plaintext highlighter-rouge">2.1.0</code>!
We have made great improvements to PSL in the areas of performance, expressively, and usability.</p>

<p>Here you will find a list of the major changes in <code class="language-plaintext highlighter-rouge">2.1.0</code> as well as information on migrating from <code class="language-plaintext highlighter-rouge">2.0.0</code>.</p>

<p>For those of you that learn better by example, check out the <a href="https://github.com/linqs/psl-examples">PSL examples repository</a>.
The <code class="language-plaintext highlighter-rouge">master</code> branch is always compatible with the most resent stable release,
while the <code class="language-plaintext highlighter-rouge">develop</code> branch stays up-to-date with our development work.</p>

<ul>
  <li><a href="#database">Database</a>
    <ul>
      <li><a href="#postgresql-database-backend">PostgreSQL Database Backend</a></li>
      <li><a href="#removed-mysql-support">Removed MySQL support</a></li>
      <li><a href="#removed-the-queries-class">Removed the Queries Class</a></li>
      <li><a href="#removed-inserterutils">Removed InserterUtils</a></li>
      <li><a href="#bulk-dataloading-in-postgres">Bulk Dataloading in Postgres</a></li>
      <li><a href="#deferred-indexing">Deferred Indexing</a></li>
      <li><a href="#no-more-predicate-deserialization">No More Predicate Deserialization</a></li>
      <li><a href="#uniqueid-storage-types">UniqueID Storage Types</a></li>
      <li><a href="#long-predicate-names-allowed">Long Predicate Names Allowed</a></li>
    </ul>
  </li>
  <li><a href="#cli">CLI</a>
    <ul>
      <li><a href="#cli-configuration-properties">CLI Configuration Properties</a></li>
      <li><a href="#complete-cli-predicate-typing">Complete CLI Predicate Typing</a></li>
      <li><a href="#literals-in-string-rules">Literals in String Rules</a></li>
    </ul>
  </li>
  <li><a href="#utilities">Utilities</a>
    <ul>
      <li><a href="#getting-the-psl-version">Getting the PSL Version</a></li>
      <li><a href="#random-in-psl">Random in PSL</a></li>
      <li><a href="#parallelization-in-psl">Parallelization in PSL</a></li>
    </ul>
  </li>
  <li><a href="#infrastructure">Infrastructure</a>
    <ul>
      <li><a href="#configuration-reworked">Configuration Reworked</a></li>
      <li><a href="#new-canary-naming-system">New Canary Naming System</a></li>
      <li><a href="#inference-api-rework">Inference API Rework</a></li>
      <li><a href="#moved-psl-evaluation">Moved PSL Evaluation</a></li>
      <li><a href="#evaluation-infrastructure-changes">Evaluation Infrastructure Changes</a></li>
      <li><a href="#external-function-support">External Function Support</a></li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="database">Database</h2>

<p>The database is the center of the grounding strategy for PSL.
Since grounding is typically the single most costly operation when running PSL, we have put a substantial amount of work into improving our database interactions.</p>

<h3 id="postgresql-database-backend">PostgreSQL Database Backend</h3>
<p>In fall of 2017, we added support for a <a href="https://www.postgresql.org/">PostgreSQL</a> database backend.
However, we have not yet publicized it.
Those who have a large number of ground rules (&gt; 1 million) will most likely benefit from using Postgres over H2 (the default).
All the details are on the <a href="https://github.com/linqs/psl/wiki/Using-PostgreSQL-with-PSL">PSL wiki page for Postgres</a>.</p>

<p>Java/Groovy users just need to use <code class="language-plaintext highlighter-rouge">org.linqs.psl.database.rdbms.driver.PostgreSQLDriver</code> instead of <code class="language-plaintext highlighter-rouge">org.linqs.psl.database.rdbms.driver.H2DatabaseDriver</code>.</p>

<p>CLI users just need to pass the <code class="language-plaintext highlighter-rouge">--postgres</code> flag with an optional argument that is the name of the database to use.</p>

<h3 id="removed-mysql-support">Removed MySQL support</h3>
<p>Support for the <a href="https://www.mysql.com/">MySQL</a> database backend has been removed.
Instead, users who need a faster database backend should use the <a href="https://github.com/linqs/psl/wiki/Using-PostgreSQL-with-PSL">Postgres database backend</a>.</p>

<h3 id="removed-the-queries-class">Removed the Queries Class</h3>
<p>The <code class="language-plaintext highlighter-rouge">Queries</code> class has been removed and all the functionality has been directly moved to the <code class="language-plaintext highlighter-rouge">ReadableDatabase</code> interface (implemented by the standard <code class="language-plaintext highlighter-rouge">RDBMSDatabse</code>).
So the same set of methods are available directly from your database.</p>

<h3 id="removed-inserterutils">Removed InserterUtils</h3>
<p><code class="language-plaintext highlighter-rouge">InserterUtils</code> (from the <code class="language-plaintext highlighter-rouge">psl-dataloading</code> package) has been removed.
Instead, you can now get the same functionality (<code class="language-plaintext highlighter-rouge">loadDelimitedData()</code> and <code class="language-plaintext highlighter-rouge">loadDelimitedDataTruth()</code>) from the <code class="language-plaintext highlighter-rouge">Inserter</code> class.
This means that Groovy users no longer will need the <code class="language-plaintext highlighter-rouge">psl-dataloading</code> dependency in you pom file.</p>

<p>Instead of something like:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Inserter</span> <span class="n">inserter</span> <span class="o">=</span> <span class="n">dataStore</span><span class="o">.</span><span class="na">getInserter</span><span class="o">(</span><span class="nc">Foo</span><span class="o">,</span> <span class="n">obsPartition</span><span class="o">);</span>
<span class="nc">InserterUtils</span><span class="o">.</span><span class="na">loadDelimitedData</span><span class="o">(</span><span class="n">inserter</span><span class="o">,</span> <span class="nc">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="no">DATA_PATH</span><span class="o">,</span> <span class="s">"foo_obs.txt"</span><span class="o">).</span><span class="na">toString</span><span class="o">());</span>
</code></pre></div></div>

<p>You can do this:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Inserter</span> <span class="n">inserter</span> <span class="o">=</span> <span class="n">dataStore</span><span class="o">.</span><span class="na">getInserter</span><span class="o">(</span><span class="nc">Foo</span><span class="o">,</span> <span class="n">obsPartition</span><span class="o">);</span>
<span class="n">inserter</span><span class="o">.</span><span class="na">loadDelimitedData</span><span class="o">(</span><span class="nc">Paths</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="no">DATA_PATH</span><span class="o">,</span> <span class="s">"foo_obs.txt"</span><span class="o">).</span><span class="na">toString</span><span class="o">());</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">loadDelimitedDataAutomatic()</code> method has also been added to the <code class="language-plaintext highlighter-rouge">Inserter</code> class.
This method has previously existed in some versions of PSL, but has not been publicized or documented.
This method will peek at the first line in the file and decide between <code class="language-plaintext highlighter-rouge">loadDelimitedData()</code> and <code class="language-plaintext highlighter-rouge">loadDelimitedDataTruth()</code>.</p>

<h3 id="bulk-dataloading-in-postgres">Bulk Dataloading in Postgres</h3>
<p>Users using the Postgres database backend will be loading data via the bulk-loading <code class="language-plaintext highlighter-rouge">COPY</code> command.
This command is specialized for fast data loading from structured sources.</p>

<p>No user intervention is required.
Calling one of the <code class="language-plaintext highlighter-rouge">loadDelimitedData*</code> methods will automatically use <code class="language-plaintext highlighter-rouge">COPY</code> if it is available, and fallback if it is not.</p>

<h3 id="deferred-indexing">Deferred Indexing</h3>
<p>In PSL, we will now defer indexing data tables until a database is actually requested via <code class="language-plaintext highlighter-rouge">DataStore.getDatabase()</code>.
In typical use cases, people will construct a <code class="language-plaintext highlighter-rouge">DataStore</code>, insert the data, and then request a database for weightlearning/inference/evaluation.
Because we now defer indexing, data loading will be speed up and our index statistics will be more up-to-date (resulting in generally better query plans).</p>

<p>No user intervention is required for this change.
However, users should be aware of this change so that they do not needlessly create databases before inserting the data.
(If you do so everything will still work as it does now, you just will not see improvements in data loading speed.)
Pre-existing data tables are assumed to already be indexed.</p>

<h3 id="no-more-predicate-deserialization">No More Predicate Deserialization</h3>
<p>A rarely used feature in past version of PSL was predicate deserialization.
With this feature, users could use an existing database without specifying the program’s predicates.
PSL would reconstruct the predicates from the structure of the database.
Unfortunately, supporting this feature significantly increases the complexity of other new features.
Therefore, we dropped predicate deserialization in favor of a simpler database structure.</p>

<p>Users who previously relied on predicate deserialization can instead just specify predicates explicitly.</p>

<h3 id="uniqueid-storage-types">UniqueID Storage Types</h3>
<p>The raw <code class="language-plaintext highlighter-rouge">UniqueID</code> type have been replaced by <code class="language-plaintext highlighter-rouge">UniqueIntID</code> and <code class="language-plaintext highlighter-rouge">UniqueStringID</code>.
This allows PSL to choose a better underlying storage type and make operations more efficient.
Using <code class="language-plaintext highlighter-rouge">UniqueStringID</code> will replicate the previous behavior and should work for any data.
However, using <code class="language-plaintext highlighter-rouge">UniqueIntID</code> will almost always be faster than <code class="language-plaintext highlighter-rouge">UniqueStringID</code>.</p>

<p>If you need to get a unique id in Java/Groovy, then you can just directly call new on it:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">UniqueIntID</span> <span class="n">myIntId</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">UniqueIntID</span><span class="o">(</span><span class="mi">4</span><span class="o">);</span>
<span class="nc">UniqueStringID</span> <span class="n">myStringId</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">UniqueStringID</span><span class="o">(</span><span class="s">"foo"</span><span class="o">);</span>
</code></pre></div></div>

<h3 id="long-predicate-names-allowed">Long Predicate Names Allowed</h3>
<p>In previous versions of PSL, predicate names were limited to a certain length (which was decided by the database backend).
PSL now allows arbitrarily long predicate names.
If the name is too long for the database backend, then a hash of the name will be used instead.</p>

<hr />

<h2 id="cli">CLI</h2>

<p>The PSL CLI has been improved to the point that it will be sufficient for most users.
Inference, weight learning, and evaluation are supported all in the CLI.
All examples in the <a href="https://github.com/linqs/psl-examples">PSL examples repository</a> have implementations in both the CLI and Groovy interfaces.
To get quick information about usage, you can invoke the CLI with the <code class="language-plaintext highlighter-rouge">--help</code> flag.</p>

<h3 id="cli-configuration-properties">CLI Configuration Properties</h3>
<p>You can now pass any PSL configuration options on the CLI command line.
Just use the <code class="language-plaintext highlighter-rouge">-D</code> option and specify the key-value pair.
For example, you can run PSL with debug logging like this:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> psl.jar <span class="nt">--infer</span> <span class="nt">--data</span> example.data <span class="nt">--model</span> example.psl <span class="nt">-D</span> log4j.threshold<span class="o">=</span>DEBUG
</code></pre></div></div>

<h3 id="complete-cli-predicate-typing">Complete CLI Predicate Typing</h3>
<p>In the CLI, you can now specify precise typing information for each predicate.
Previously, all predicate arguments in the CLI were typed as <code class="language-plaintext highlighter-rouge">UniqueStringID</code>.
The <a href="https://github.com/linqs/psl/wiki/CLI-Data-File-Format">CLI data file wiki page</a> contains all the information.</p>

<h3 id="literals-in-string-rules">Literals in String Rules</h3>
<p>Literals in string rules have been expanded to generally accept all characters.
All literals (even numeric ones) must be quoted by single or double quotes.
PSL will convert the literal to its underlying storage type.</p>

<p>If a literal contains the same quote used at the start/end, then is must be escaped with a backslash.
Backslashes must always be escaped.</p>

<p>The following escapes are understood:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">\'</code> - Literal Single Quote</li>
  <li><code class="language-plaintext highlighter-rouge">\"</code> - Literal Double Quote</li>
  <li><code class="language-plaintext highlighter-rouge">\\</code> - Literal Backslash</li>
  <li><code class="language-plaintext highlighter-rouge">\t</code> - Tab</li>
  <li><code class="language-plaintext highlighter-rouge">\n</code> - Newline</li>
  <li><code class="language-plaintext highlighter-rouge">\r</code> - Carriage Return</li>
</ul>

<hr />

<h2 id="utilities">Utilities</h2>

<h3 id="getting-the-psl-version">Getting the PSL Version</h3>
<p>You can now get the version of PSL you are using.</p>

<p>Java/Groovy:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">org</span><span class="o">.</span><span class="na">linqs</span><span class="o">.</span><span class="na">psl</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Version</span><span class="o">.</span><span class="na">get</span><span class="o">()</span>
</code></pre></div></div>

<p>CLI:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> psl.jar <span class="nt">--version</span>
</code></pre></div></div>

<h3 id="random-in-psl">Random in PSL</h3>
<p>Random values can now be statically fetched from the <code class="language-plaintext highlighter-rouge">org.linqs.psl.util.RandUtils</code> class.
This class is thread-safe.
Seeds can be set using the <code class="language-plaintext highlighter-rouge">RandUtils.seed</code> method or by setting the <code class="language-plaintext highlighter-rouge">random.seed</code> property.</p>

<h3 id="parallelization-in-psl">Parallelization in PSL</h3>
<p>PSL now leverages multithreading in grounding and term generation.
By default, PSL uses all the available threads on the machine.
To set the number of threads, use the <code class="language-plaintext highlighter-rouge">parallel.numthreads</code> property.</p>

<p>Those writing code for PSL can take advantage of our parallelization infrastructure in the <code class="language-plaintext highlighter-rouge">org.linqs.psl.util.Parallel</code> class.</p>

<hr />

<h2 id="infrastructure">Infrastructure</h2>

<h3 id="configuration-reworked">Configuration Reworked</h3>
<p>Both the <code class="language-plaintext highlighter-rouge">ConfigManager</code> and <code class="language-plaintext highlighter-rouge">ConfigBundle</code> have been removed and replaced with the static-only <code class="language-plaintext highlighter-rouge">Config</code> class.
The same methods are all supported.</p>

<p>Instead of:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.linqs.psl.config.ConfigBundle</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.linqs.psl.config.ConfigManager</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
   <span class="nc">ConfigManager</span> <span class="n">manager</span> <span class="o">=</span> <span class="nc">ConfigManager</span><span class="o">.</span><span class="na">getManager</span><span class="o">();</span>
   <span class="nc">ConfigBundle</span> <span class="n">config</span> <span class="o">=</span> <span class="n">manager</span><span class="o">.</span><span class="na">getBundle</span><span class="o">(</span><span class="s">"mybundle"</span><span class="o">);</span>

   <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">config</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">"key"</span><span class="o">,</span> <span class="s">"default"</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>

<p>You can now do:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.linqs.psl.config.Config</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
   <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">Config</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">"key"</span><span class="o">,</span> <span class="s">"default"</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="new-canary-naming-system">New Canary Naming System</h3>
<p>The purpose of having a single build named <code class="language-plaintext highlighter-rouge">CANARY</code> is so that anyone can always grab just that build and be confident they have the latest published build.
However, the lack of version number can be confusing.
So as of now, we will always push two canary builds: one versioned and one unversioned.
The unversioned one will continue to be called <code class="language-plaintext highlighter-rouge">CANARY</code>.
The versioned one will have a suffix matching the next release and the number of canary builds pushed.
For example, this first versioned canary is called <code class="language-plaintext highlighter-rouge">CANARY-2.1.0</code>, the next one is <code class="language-plaintext highlighter-rouge">CANARY-2.1.1</code> and so on.</p>

<p>This way, those that want development improvement but a more static environment can use a versioned canary.
To check what canary versions are available, just look at our <a href="https://linqs-data.soe.ucsc.edu/maven/repositories/psl-releases/org/linqs/psl-groovy/">maven repository</a>.
I have retroactively versioned the canary released on December 8th as <code class="language-plaintext highlighter-rouge">CANARY-2.1.0</code>.</p>

<p>For more details on the canary builds, checkout the <a href="https://github.com/linqs/psl/wiki/Working-With-Canary">PSL canary wiki page</a>.</p>

<h3 id="inference-api-rework">Inference API Rework</h3>
<p>Now all inference methods inherit from <code class="language-plaintext highlighter-rouge">org.linqs.psl.application.inference.InferenceApplication</code>.
The <code class="language-plaintext highlighter-rouge">inference()</code> method (previously <code class="language-plaintext highlighter-rouge">mpeInference()</code>) now does not return anything.
If you need information about how inference ended, then you can query for the components of inference using:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">getGroundRuleStore()</code></li>
  <li><code class="language-plaintext highlighter-rouge">getTermStore()</code></li>
  <li><code class="language-plaintext highlighter-rouge">getReasoner()</code></li>
</ul>

<h3 id="moved-psl-evaluation">Moved PSL Evaluation</h3>
<p>To support evaluation in the CLI and more general scoring in search-based weight learning, the <code class="language-plaintext highlighter-rouge">psl-evaluation</code> package has been moved from the <code class="language-plaintext highlighter-rouge">psl-utils</code> repository into the <code class="language-plaintext highlighter-rouge">psl-core</code> package.
For most users, the only change is to remove <code class="language-plaintext highlighter-rouge">psl-evaluation</code> from their <code class="language-plaintext highlighter-rouge">pom.xml</code> file.</p>

<p>Any users using more obscure evaluation methods from the old <code class="language-plaintext highlighter-rouge">psl-evaluation</code> can still find them in the <code class="language-plaintext highlighter-rouge">psl-evaluation-extended</code> package (still in <code class="language-plaintext highlighter-rouge">psl-utils</code>).
Over time, this package will be phased out as we standardize all the evaluation methods.</p>

<h3 id="evaluation-infrastructure-changes">Evaluation Infrastructure Changes</h3>
<p>All the PSL evaluation infrastructure has been reworked.
Now all evaluation classes (previously called <code class="language-plaintext highlighter-rouge">Comparator</code> now called <code class="language-plaintext highlighter-rouge">Evaluator</code>) all follow the same interface.</p>

<p>Java/Groovy users can follow the general pattern:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Evaluator</span> <span class="n">eval</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ContinuousEvaluator</span><span class="o">();</span>
<span class="n">eval</span><span class="o">.</span><span class="na">compute</span><span class="o">(</span><span class="n">resultsDB</span><span class="o">,</span> <span class="n">truthDB</span><span class="o">,</span> <span class="nc">MyTargetPredicate</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">eval</span><span class="o">.</span><span class="na">getAllStats</span><span class="o">());</span>
</code></pre></div></div>

<p>CLI users can use the <code class="language-plaintext highlighter-rouge">--eval</code> option and specify the type of evaluator to use:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> psl.jar <span class="nt">--infer</span> <span class="nt">--data</span> example.data <span class="nt">--model</span> example.psl <span class="nt">--eval</span> org.linqs.psl.evaluation.statistics.ContinuousEvaluator
</code></pre></div></div>

<p>The <a href="https://github.com/linqs/psl-examples">PSL examples</a> show how to use the evaluation infrastructure (in both Groovy and CLI).</p>

<h3 id="external-function-support">External Function Support</h3>
<p>External functions have been reworked to not rely on the backend database.
This means that all database backends now support external functions with no additional work.</p>

<p>In addition, the database abstraction passed to external functions has been changed from a <code class="language-plaintext highlighter-rouge">ReadonlyDatabase</code> to a <code class="language-plaintext highlighter-rouge">ReadableDatabase</code>.
This allows external functions to access more database functionality.</p>]]></content><author><name>Eriq Augustine</name></author><category term="release" /><category term="v2.1.0" /><summary type="html"><![CDATA[We are happy to announce the release of PSL version 2.1.0! We have made great improvements to PSL in the areas of performance, expressively, and usability.]]></summary></entry><entry><title type="html">Getting Started with PSL</title><link href="/blog/2018/07/15/getting-started-with-psl.html" rel="alternate" type="text/html" title="Getting Started with PSL" /><published>2018-07-15T17:00:00+00:00</published><updated>2018-07-15T17:00:00+00:00</updated><id>/blog/2018/07/15/getting-started-with-psl</id><content type="html" xml:base="/blog/2018/07/15/getting-started-with-psl.html"><![CDATA[<p>Probabilistic Soft Logic (PSL) is a <a href="https://en.wikipedia.org/wiki/Statistical_relational_learning">Statistical Relational Learning (SRL)</a> framework.
It is intended to be usable by people at all levels of Computer Science and Machine Learning.</p>

<p>PSL currently has two primary interfaces: a command line interface (CLI) and a <a href="https://en.wikipedia.org/wiki/Apache_Groovy">Groovy</a> interface.
In this tutorial, we will cover the command line interface.
However, all the examples we reference also have implemented Groovy versions on the same repository.</p>

<h3 id="setup">Setup</h3>

<p>For the CLI, the only hard requirement is that you have Java 7 or 8 installed.
However we will be using some scripts meant for UNIX-style systems (Linux and Mac), so Windows users will have <a href="/faq/#can-psl-run-on-windows">some additional steps</a>.
We will also be using git to fetch some examples, but users without git can just use a browser to go to the repository and download it directly.</p>

<h3 id="getting-the-code">Getting the code</h3>

<p>As with all the PSL examples, you can find all the code in our <a href="https://github.com/linqs/psl-examples">psl-examples repository</a>.
For this tutorial, we will be using the <code class="language-plaintext highlighter-rouge">simple-acquaintances</code> example.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/linqs/psl-examples.git
<span class="nb">cd </span>psl-examples/simple-acquaintances/cli
</code></pre></div></div>

<h3 id="run-your-first-psl-program">Run your first PSL program</h3>

<p>The <code class="language-plaintext highlighter-rouge">simple-acquaintances</code> example is a toy model with the goal of inferring whether or not a pair of people already know each other.
To do this, we will use information about the locations of some people, who people know already, and what people enjoy to infer who knows each other.
Before we dive into the specifics of the model, lets just get it running.</p>

<p>All examples come with a <code class="language-plaintext highlighter-rouge">run.sh</code> script that should handle everything for us.
So on the command line, invoke the run script:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./run.sh
</code></pre></div></div>

<p>The PSL jar will be fetched automatically.
You can also select what version of PSL is fetched/used at the top of this script.</p>

<p>You should now see output that looks like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Running PSL Inference
0    [main] INFO  org.linqs.psl.cli.Launcher  - Loading data
82   [main] INFO  org.linqs.psl.cli.Launcher  - Data loading complete
82   [main] INFO  org.linqs.psl.cli.Launcher  - Loading model
178  [main] INFO  org.linqs.psl.cli.Launcher  - Model loading complete
178  [main] INFO  org.linqs.psl.cli.Launcher  - Starting inference with class: org.linqs.psl.application.inference.MPEInference
243  [main] INFO  org.linqs.psl.application.inference.MPEInference  - Grounding out model.
334  [main] INFO  org.linqs.psl.application.inference.MPEInference  - Beginning inference.
448  [main] INFO  org.linqs.psl.reasoner.admm.ADMMReasoner  - Optimization completed in 405 iterations. Primal res.: 0.022973757, Dual res.: 6.7510834E-4
448  [main] INFO  org.linqs.psl.application.inference.MPEInference  - Inference complete. Writing results to Database.
472  [main] INFO  org.linqs.psl.application.inference.MPEInference  - Results committed to database.
472  [main] INFO  org.linqs.psl.cli.Launcher  - Inference Complete
477  [main] INFO  org.linqs.psl.cli.Launcher  - Starting evaluation with class: org.linqs.psl.evaluation.statistics.DiscreteEvaluator.
493  [main] INFO  org.linqs.psl.cli.Launcher  - Evaluation results for KNOWS -- Accuracy: 0.596154, F1: 0.676923, Positive Class Precision: 0.733333, Positive Class Recall: 0.628571, Negative Class Precision: 0.409091, Negative Class Recall: 0.529412
493  [main] INFO  org.linqs.psl.cli.Launcher  - Evaluation complete.
</code></pre></div></div>

<h3 id="where-are-the-predictions">Where are the predictions?</h3>

<p>By default, the PSL examples output the results into the <code class="language-plaintext highlighter-rouge">inferred-predicates</code> directory.
The results for this program will looks something like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat inferred-predicates/KNOWS.txt | sort
'Alex'  'Arti'    0.9966434240341187
'Alex'  'Ben'     0.5923646092414856
'Alex'  'Dhanya'  0.48785600066185
'Alex'  'Elena'   0.7109028697013855
'Alex'  'Jay'     0.6563224792480469
'Alex'  'Sabina'  0.5790407657623291
&lt; --- 40 lines omitted for brevity --- &gt;
'Steve'  'Arti'   0.5648082494735718
'Steve'  'Ben'    0.4413864314556122
'Steve'  'Dhanya' 0.4964808523654938
'Steve'  'Elena'  0.4955149292945862
'Steve'  'Jay'    0.6143320798873901
'Steve'  'Sabina' 0.5134021043777466
</code></pre></div></div>

<h3 id="what-did-it-do">What did it do?</h3>

<p>Now that we’ve run our first program that performs link prediction to infer who knows who, let’s understand the steps that we went through to infer the unknown values:
defining the underlying model, providing data to the model, and running inference to classify the unknown values.</p>

<h4 id="defining-a-model">Defining a Model</h4>

<p>A model in PSL is a set of logic-like rules.</p>

<p>The model is defined inside a text file with the format <code class="language-plaintext highlighter-rouge">.psl</code>.
We describe this model in the file <code class="language-plaintext highlighter-rouge">simple-acquaintances.psl</code>.</p>

<p>Let’s have a look at the rules that make up our model:</p>
<div class="language-prolog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="m">20</span><span class="o">:</span> <span class="nv">Lived</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">L</span><span class="p">)</span> <span class="o">&amp;</span> <span class="nv">Lived</span><span class="p">(</span><span class="nv">P2</span><span class="p">,</span> <span class="nv">L</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="nv">P1</span> <span class="p">!</span><span class="o">=</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Knows</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">^</span><span class="m">2</span>
<span class="m">5</span><span class="o">:</span> <span class="nv">Lived</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">L1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="nv">Lived</span><span class="p">(</span><span class="nv">P2</span><span class="p">,</span> <span class="nv">L2</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="nv">P1</span> <span class="p">!</span><span class="o">=</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="nv">L1</span> <span class="p">!</span><span class="o">=</span> <span class="nv">L2</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">!</span><span class="nv">Knows</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">^</span><span class="m">2</span>
<span class="m">10</span><span class="o">:</span> <span class="nv">Likes</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">L</span><span class="p">)</span> <span class="o">&amp;</span> <span class="nv">Likes</span><span class="p">(</span><span class="nv">P2</span><span class="p">,</span> <span class="nv">L</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="nv">P1</span> <span class="p">!</span><span class="o">=</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Knows</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">^</span><span class="m">2</span>
<span class="m">5</span><span class="o">:</span> <span class="nv">Knows</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">&amp;</span> <span class="nv">Knows</span><span class="p">(</span><span class="nv">P2</span><span class="p">,</span> <span class="nv">P3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="nv">P1</span> <span class="p">!</span><span class="o">=</span> <span class="nv">P3</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nv">Knows</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">P3</span><span class="p">)</span> <span class="o">^</span><span class="m">2</span>
<span class="nv">Knows</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">=</span> <span class="nv">Knows</span><span class="p">(</span><span class="nv">P2</span><span class="p">,</span> <span class="nv">P1</span><span class="p">)</span> <span class="p">.</span>
<span class="m">5</span><span class="o">:</span> <span class="p">!</span><span class="nv">Knows</span><span class="p">(</span><span class="nv">P1</span><span class="p">,</span> <span class="nv">P2</span><span class="p">)</span> <span class="o">^</span><span class="m">2</span>
</code></pre></div></div>

<p>The model is expressing the intuition that people who have lived in the same location or like the same thing may know each other.
The values at the beginning of rules indicate the weight of the rule.
Intuitively, this tells us the relative importance of satisfying this rule compared to the other rules.
(Weight only matters relative to other rules, not in an absolute sense.)
The <code class="language-plaintext highlighter-rouge">^2</code> at the end of the rules indicates that the hinge-loss function for this rule should be squared.
This makes for a smoother tradeoff between conflicting values.</p>

<p>For a full description of rule syntax, see the <a href="https://github.com/linqs/psl/wiki/Rule-Specification">Rule Specification in the wiki</a>.</p>

<h4 id="loading-the-data">Loading the Data</h4>

<p>PSL rules consist of predicates joined by logical operations.
The names of the predicates and the data to load into them are defined inside the file <code class="language-plaintext highlighter-rouge">simple-acquaintances.data</code>.</p>

<p>Let’s have a look:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">predicates</span><span class="pi">:</span>
  <span class="na">Knows/2</span><span class="pi">:</span> <span class="s">open</span>
  <span class="na">Likes/2</span><span class="pi">:</span> <span class="s">closed</span>
  <span class="na">Lived/2</span><span class="pi">:</span> <span class="s">closed</span>

<span class="na">observations</span><span class="pi">:</span>
  <span class="na">Knows </span><span class="pi">:</span> <span class="s">../data/knows_obs.txt</span>
  <span class="na">Lived </span><span class="pi">:</span> <span class="s">../data/lived_obs.txt</span>
  <span class="na">Likes </span><span class="pi">:</span> <span class="s">../data/likes_obs.txt</span>

<span class="na">targets</span><span class="pi">:</span>
  <span class="na">Knows </span><span class="pi">:</span> <span class="s">../data/knows_targets.txt</span>

<span class="na">truth</span><span class="pi">:</span>
  <span class="na">Knows </span><span class="pi">:</span> <span class="s">../data/knows_truth.txt</span>
</code></pre></div></div>

<p>In the <code class="language-plaintext highlighter-rouge">predicates</code> section, we list all the predicates that will be used in rules for this model.
The keyword <code class="language-plaintext highlighter-rouge">open</code> indicates that we want to infer some values of this predicate while <code class="language-plaintext highlighter-rouge">closed</code> indicates that this predicate is fully observed.
I.e. all substitutions of this predicate have known values and will behave as evidence for inference.</p>

<p>For our simple example, we fully observe where people have lived (<code class="language-plaintext highlighter-rouge">Lived</code>) and what things they like or dislike (<code class="language-plaintext highlighter-rouge">Likes</code>).
Thus, <code class="language-plaintext highlighter-rouge">Likes</code> and <code class="language-plaintext highlighter-rouge">Lived</code> are both closed predicates.
We are aware of some instances of people knowing each other, but wish to infer the other instances.
Therefore, <code class="language-plaintext highlighter-rouge">Knows</code> an open predicate.</p>

<p>In the <code class="language-plaintext highlighter-rouge">observations</code> section, for each predicate that we have observations on, we specify the name of the tab-separated file containing the observations.
For example, <code class="language-plaintext highlighter-rouge">knows_obs.txt</code> and <code class="language-plaintext highlighter-rouge">lived_obs.txt</code> specifies which people know each other and where some of these people live, respectively.</p>

<p>The <code class="language-plaintext highlighter-rouge">targets</code> section specifies a file that, for each open predicate, lists all substitutions of that predicate that we wish to infer.
In <code class="language-plaintext highlighter-rouge">knows_targets.txt</code>, we specify the pairs of people for whom we wish to infer.</p>

<p>The <code class="language-plaintext highlighter-rouge">truth</code> section specifies a file that provides a set of ground truth observations for each open predicate.
Here, we give the actual values for the <code class="language-plaintext highlighter-rouge">Knows</code> predicate for all the people in the network as training labels.
We describe the general data loading scheme in more detail in the sections below.
Truth data is only necessary if you are running weight learning or evaluation.</p>

<h4 id="writing-psl-rules">Writing PSL Rules</h4>

<p>To create a PSL model, you should define a set of rules in a <code class="language-plaintext highlighter-rouge">.psl</code> file.
Let’s go over the basic syntax to write rules. Consider this very general rule form:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>w: P(A,B) &amp; Q(B,C) -&gt; R(A,C) ^2
</code></pre></div></div>

<p>The first part of the rule, <code class="language-plaintext highlighter-rouge">w</code>, is a float value that specifies the weight of the rule.
In this example, <code class="language-plaintext highlighter-rouge">P</code>, <code class="language-plaintext highlighter-rouge">Q</code> and <code class="language-plaintext highlighter-rouge">R</code> are predicates.
Logical rules consist of the rule “body” and the rule “head.”
The body of the rule appears before the <code class="language-plaintext highlighter-rouge">-&gt;</code> which denotes logical implication.
The body can have one or more predicates conjuncted together with the logical conjunction operator: <code class="language-plaintext highlighter-rouge">&amp;</code>.
The head can have one or more predicates disjuncted together with the logical disjunction operator: <code class="language-plaintext highlighter-rouge">|</code>.
The predicates that appear in the body and head can be any combination of open and closed predicate types.</p>

<p>The <a href="https://github.com/linqs/psl/wiki/Rule-Specification">Rule Specification wiki page</a> contains the full syntax for PSL rules.</p>

<h4 id="organizing-your-data">Organizing your Data</h4>

<p>In a <code class="language-plaintext highlighter-rouge">.data</code> file, you should first define your <code class="language-plaintext highlighter-rouge">predicates:</code> as shown in the above example.
Use the <code class="language-plaintext highlighter-rouge">open</code> and <code class="language-plaintext highlighter-rouge">closed</code> keywords to characterize each predicate.</p>

<p>A <code class="language-plaintext highlighter-rouge">closed</code> predicate is a predicate whose values are always observed.
For example, the <code class="language-plaintext highlighter-rouge">knows</code> predicate from the simple example is closed because we fully observe the entire network of people that know one another.
On the other hand, an <code class="language-plaintext highlighter-rouge">open</code> predicate is a predicate where some values may be observed, but some values are missing and thus, need to be inferred.</p>

<p>As shown above, then create your <code class="language-plaintext highlighter-rouge">observations:</code>, <code class="language-plaintext highlighter-rouge">targets:</code> and <code class="language-plaintext highlighter-rouge">truth:</code> sections that list the names of files that specify the observed values for predicates, values you want to infer for open predicates, and observed ground truth values for open predicates.</p>

<p>For all predicates, all possible substitutions should be specified either in the target files or in the observation files.
The observations files should contain the known values for all closed predicates and can contain some of the known values for the open predicates.
The target files tell PSL which substitutions of the open predicates it needs to infer.
Target files cannot be specified for closed predicates as they are fully observed.</p>

<p>The truth files provide training labels in order learn the weights of the rules directly from data.
This is similar to learning the weights of coefficients in a logistic regression model from training data.</p>

<h4 id="running-inference">Running Inference</h4>

<p>Run inference with the general command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> psl-cli.jar <span class="nt">--infer</span> <span class="nt">--model</span> <span class="o">[</span>name of model file].psl <span class="nt">--data</span> <span class="o">[</span>name of data file].data
</code></pre></div></div>

<p>When we run inference, the inferred values are outputted to the screen.
If you want to write the outputs to a file and use the inferred values in various ways downstream, you can use:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> psl-cli.jar <span class="nt">--infer</span> <span class="nt">--model</span> <span class="o">[</span>name of model file].psl <span class="nt">--data</span> <span class="o">[</span>name of data file].data <span class="nt">--output</span> <span class="o">[</span>directory to write output files]
</code></pre></div></div>

<p>Values for all predicates will be output as tab-separated files in the specified output directory.</p>

<p>With the inferred values, some downstream tasks that you can perform are:</p>
<ul>
  <li>if you have a gold standard set of labels, you can evaluate your model by computing standard metrics like accuracy, AUC, F1, etc.</li>
  <li>you may want to use the predicted outputs of PSL as inputs for another model.</li>
  <li>you may want to visualize the predicted values and use the outputs of PSL as inputs to a data visualization program.</li>
</ul>

<h3 id="more-psl-resources">More PSL Resources</h3>

<p>For more information on PSL, you can check out:</p>
<ul>
  <li><a href="/pubs">Key Papers</a></li>
  <li><a href="https://groups.google.com/forum/#!forum/psl-users">Support Forum</a></li>
  <li><a href="/code">Source Code</a></li>
  <li><a href="https://github.com/linqs/psl-examples">PSL Examples</a></li>
</ul>]]></content><author><name>Eriq Augustine</name></author><category term="tutorial" /><category term="v2.1.0" /><summary type="html"><![CDATA[Probabilistic Soft Logic (PSL) is a Statistical Relational Learning (SRL) framework. It is intended to be usable by people at all levels of Computer Science and Machine Learning.]]></summary></entry></feed>