<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="http://joseangelfernandez.es/feed.xml" rel="self" type="application/atom+xml" /><link href="http://joseangelfernandez.es/" rel="alternate" type="text/html" /><updated>2024-08-02T09:27:21+00:00</updated><id>http://joseangelfernandez.es/feed.xml</id><title type="html">joseangelfernandez.es</title><subtitle>solo otro blog más…</subtitle><author><name>José Ángel Fernández</name></author><entry><title type="html">Automatiza el despliegue de tu servidor CycleCloud con Bicep</title><link href="http://joseangelfernandez.es/blog/automatizar-despliegue-cyclecloud-bicep.html" rel="alternate" type="text/html" title="Automatiza el despliegue de tu servidor CycleCloud con Bicep" /><published>2022-10-27T00:00:00+00:00</published><updated>2022-10-27T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/automatizar-despliegue-cyclecloud-bicep</id><content type="html" xml:base="http://joseangelfernandez.es/blog/automatizar-despliegue-cyclecloud-bicep.html"><![CDATA[<p><a href="https://learn.microsoft.com/en-us/azure/cyclecloud/overview?view=cyclecloud-8">CycleCloud</a> proporciona la forma más sencilla de aprovisionar en Azure clústeres de computación de alto rendimiento <em>(High Performance Computing, HPC)</em> basados en los planificadores más utilizados en el mercado como por ejemplo: Slurm, PBS o LSF.</p>

<p>De forma general, CycleCloud se despliega como una máquina virtual a partir de la <a href="https://azuremarketplace.microsoft.com/en-us/marketplace/apps/azurecyclecloud.azure-cyclecloud?tab=overview">imagen base disponible en el Marketplace de Azure</a>. Tras ello, para completar la instalación, es necesario seguir un asistente web de tres sencillos pasos.</p>

<p>En la mayor parte de los casos, este despliegue manual es suficiente ya que es un proceso que se realiza una única vez. Sin embargo, en algunos casos es posible que nos interese automatizar el proceso de instalación sin necesidad de acceder a la interfaz gráfica. Especialmente aquellos clientes que emplean la estrategia de <em>Infraestructura como Código (IaC)</em> para desplegar múltiples entornos u entornos efímeros.</p>

<p>Este artículo se basa en los scripts de configuración de Ansible empleados por <a href="https://github.com/Azure/az-hop">Azure HPC On-Demand Platform</a> y permite desplegar tu servidor de CycleCloud de forma automática. En primer lugar, se explica paso a paso la configuración necesaria para a continuación proporcionar una implementación mínima en Bicep lista para ser desplegada.</p>

<h2 id="información-necesaria-para-la-configuración-inicial">Información necesaria para la configuración inicial</h2>

<p>Para tener una instalación funcional de CycleCloud es necesario proporcionar los siguientes datos:</p>

<ul>
  <li>Nombre del usuario y contraseña de la cuenta de usuario inicial que tendrá el rol administrador del servidor.</li>
  <li>Clave SSH pública asociada a dicho usuario que se empleará para configurar el acceso remoto a los nodos de los clústeres.</li>
  <li>Datos de la subscripción de Azure que se utilizará para desplegar los recursos asociados a los clústeres.</li>
</ul>

<h2 id="configurar-la-cuenta-de-administrador">Configurar la cuenta de administrador</h2>

<p>CycleCloud permite modificar la configuración del servidor de forma dinámica a través de ficheros JSON. Estos ficheros, al ser colocados en la carpeta <em>/data/config</em> dentro del directorio de instalación de CycleCloud son automáticamente importados. Si la configuración se ha importado correctamente veremos que el nombre del fichero se habrá modificado con la extensión <em>.json.imported</em>. Este funcionalidad permite  proporcionar los datos necesarios para automatizar la instalación.</p>

<p>En primer lugar, para configurar la cuenta de administador crearemos un nuevo fichero que llamaremos account_data.json. El nombre es indiferente por lo que es posible escoger cualquier otro nombre. El fichero será necesario crearlo en <em>/opt/cycle_server/config/data/</em>.</p>

<p>El contenido será el siguiente:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>
  <span class="o">{</span>
    <span class="s2">"AdType"</span>: <span class="s2">"Application.Setting"</span>,
    <span class="s2">"Name"</span>: <span class="s2">"cycleserver.installation.initial_user"</span>,
    <span class="s2">"Value"</span>: &lt;YourUserName&gt;
  <span class="o">}</span>,
  <span class="o">{</span>
    <span class="s2">"AdType"</span>: <span class="s2">"AuthenticatedUser"</span>,
    <span class="s2">"Name"</span>: &lt;YourUserName&gt;
    <span class="s2">"RawPassword"</span>: &lt;YourUserPassword&gt;,
    <span class="s2">"Superuser"</span>: <span class="nb">true</span>
  <span class="o">}</span>,
  <span class="o">{</span>
    <span class="s2">"AdType"</span>: <span class="s2">"Credential"</span>,
    <span class="s2">"CredentialType"</span>: <span class="s2">"PublicKey"</span>,
    <span class="s2">"Name"</span>: <span class="s2">"&lt;YouUserName&gt;/public"</span>
    <span class="s2">"PublicKey"</span>: &lt;YourPublicKeyInformation&gt;,
  <span class="o">}</span>,
  <span class="o">{</span>
    <span class="s2">"AdType"</span>: <span class="s2">"Application.Setting"</span>,
    <span class="s2">"Name"</span>: <span class="s2">"cycleserver.installation.complete"</span>,
    <span class="s2">"Value"</span>: <span class="nb">true</span>
  <span class="o">}</span>
<span class="o">]</span>
</code></pre></div></div>

<p>En primer lugar se esvoge el nombre de usuario administrador de CycleCloud, su contraseña y la clave de acceso pública asociada. La última propiedad indica a CycleCloud que no muestre la pantalla de configuración inicial ya que la configuración se ha realizado correctamente.</p>

<p>Una vez guardado el fichero, comprobaremos que su extensión es modificada a <em>.imported</em> indicando que los cambios se han aplicado.</p>

<h2 id="configurar-la-subscripción-de-azure">Configurar la subscripción de Azure</h2>

<p>Tras configurar el administrador de CycleCloud, el siguiente paso es proporcionar los datos de la suscripción de Azure que usaremos para desplegar los clústeres. Crearemos un fichero JSON que llamaremos azure_data.json. Esta vez, sin embargo, no lo haremos en dicho directorio sino en nuestra $HOME.</p>

<p>El contenido del fichero será el siguiente:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">{</span>
    <span class="s2">"Environment"</span>: <span class="s2">"public"</span>,
    <span class="s2">"AzureRMUseManagedIdentity"</span>: <span class="nb">true</span>,
    <span class="s2">"AzureResourceGroup"</span>: &lt;ResourceGroupWhereForCycleCloudResources&gt;,
    <span class="s2">"AzureRMApplicationId"</span>: <span class="s2">" "</span>,
    <span class="s2">"AzureRMApplicationSecret"</span>: <span class="s2">" "</span>,
    <span class="s2">"AzureRMSubscriptionId"</span>: &lt;AzureSubscriptionId&gt;,
    <span class="s2">"AzureRMTenantId"</span>: &lt;AzureTenantId&gt;,
    <span class="s2">"DefaultAccount"</span>: <span class="nb">true</span>,
    <span class="s2">"Location"</span>: &lt;DefaultLocation&gt;,
    <span class="s2">"Name"</span>: <span class="s2">"azure"</span>,
    <span class="s2">"Provider"</span>: <span class="s2">"azure"</span>,
    <span class="s2">"ProviderId"</span>: <span class="s2">"fd6abe95-c55e-44c8-9085-68002a27c1bb"</span>,
    <span class="s2">"RMStorageAccount"</span>: &lt;ExistinStorageAccountName&gt;
    <span class="s2">"RMStorageContainer"</span>: &lt;CycleCloudLockerContainerName&gt;
  <span class="o">}</span>

</code></pre></div></div>

<p>Es importante mencionar que CycleCloud soporta dos formas de autenticarse contra Azure:</p>
<ul>
  <li>Utilizando un Service Principal (Application) y su contraseña (Secret)</li>
  <li>Utilizando una identidad gestionada por Azure.</li>
</ul>

<p>La segunda opción es la recomendada ya que toda la gestión del ciclo de vida de la identidad y de los secretos es gestionada de forma automática por Azure. En este ejemplo utilizaremos esta opción. Si por algún motivo es necesario utilizar un <em>Service Principal</em>, únicamente tendrás que proporcionar sus datos en las propiedades <em>AzureRMApplicationId</em> y <em>AzureRMApplicationSecret</em>, y modificar a <em>false</em> la propiedad <em>AzureRMUseManagedIdentity</em></p>

<p>No obstante, en cualquiera de las dos opciones es importante acordarse de asignar los permisos de RBAC dentro de nuestra suscripción para desplegar los recursos. En este ejemplo utilizaremos el rol de <em>Contributor</em> a nivel de la suscripción.</p>

<p>Tras asegurarnos que los permisos están correctamente configurados será necesario ejecutar el siguiente comando.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/usr/local/bin/cyclecloud account create <span class="nt">-f</span> <span class="nv">$HOME</span>/azure_data.json
</code></pre></div></div>

<p>Tras ello, si accedemos al portal de CycleCloud con los datos del usuario administrador, podremos ver que funcionan y que en la opción de Configuración (1) &gt; Suscripciones (2), la suscripción se ha configurado correctamente (3) y está recuperando la información necesaria (4).</p>

<p><a href="/assets/img/cyclecloudinstallation.png"><img src="/assets/img/cyclecloudinstallation.png" alt="" /></a></p>

<h2 id="un-ejemplo-mínimo-de-automatización-con-bicep">Un ejemplo mínimo de automatización con Bicep</h2>

<p>Todo el proceso anterior ha servido para entender cuáles son los pasos necesarios para automatizar la instalación sin hacer uso de la interfaz de usuario. Sin embargo, hemos seguido realizando de forma manual cada uno de los pasos. Esto son:</p>

<ol>
  <li>Crear la máquina virtual desde el MarketPlace con la imagen de referencia de CycleCloud</li>
  <li>Configurar la máquina virtual para que haga uso de las identidades gestionadas</li>
  <li>Asignar los permisos RBAC a la identidad gestionada para que tenga acceso a la suscripción de Azure</li>
  <li>Generar el fichero de configuración del usuario administrador de CycleCloud en la carpeta <em>/data/config</em>.</li>
  <li>Importar la configuración de Azure con el comando <em>cyclecloud account</em></li>
</ol>

<p>Los tres primeros puntos es posible automatizarlos con una plantilla de Bicep. Los dos últimos podemos escribir nuestros propios scripts, o, como comentábamos al principio, aprovechar el trabajo existente de az-hop.</p>

<p>No queremos reinventar la rueda por lo que usaremos el fichero <a href="https://github.com/Azure/az-hop/blob/main/playbooks/roles/cyclecloud/files/configure.py">configure.py</a> que incorpora además validaciones extras ante posibles fallos y la inicialización de la CLI de CycleCloud. Para nuestro caso particular, únicamente será necesario modificar la línea 230 para reemplazar:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">--url</span><span class="o">=</span>https://localhost/cyclecloud<span class="s2">"
</span></code></pre></div></div>
<p>por</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">--url</span><span class="o">=</span>https://localhost/<span class="s2">"
</span></code></pre></div></div>

<p>ya que la instalación por defecto es a nivel raíz del servidor.</p>

<p>Si quieres ver el código, está disponible en <a href="https://github.com/jangelfdez/cyclecloud-bicep">jangelfdez/cyclecloud-bicep</a>. Para desplegarlo en tu suscripción, únicamente necesitarás ejecutar el siguiente comando reemplazando los valores de los siguiente parámetros:</p>

<ul>
  <li><strong>location</strong>: región de Azure donde se realizará el despliegue.</li>
  <li><strong>resourceGroupname</strong>: grupo de recursos donde se creará la máquina virtual de CycleCloud.</li>
  <li><strong>vnetName</strong> y <strong>subnetName</strong>: datos de la red donde la máquina virtual se desplegará.</li>
  <li><strong>vnetResourceGroupName</strong>: si vuestra red virtual está en otro grupo de recursos diferente, es necesario indicarlo con este parámetro. Si está en el mismo lo puedes omitir.</li>
  <li><strong>storageAccountName</strong>: nombre de la cuenta de almacenamiento que CycleCloud utilizará como <em>locker</em>.</li>
  <li><strong>tenantId</strong>: identificador de vuestro tenant <em>*.onmicrosoft.com</em>.</li>
  <li><strong>adminUsername</strong>, <strong>adminPassword</strong>, <strong>publicKey</strong>: los datos de acceso del usuario administrador de CycleCloud que coincidirán con los de la VM en este caso.</li>
</ul>

<pre><code class="language-bicep">
az deployment sub create --location &lt;location&gt; --template-file .\main.bicep --parameters resourceGroupName=&lt;rgName&gt; vnetName=&lt;vnetName&gt; subnetName=&lt;subnetName&gt; vnetResourceGroupName=&lt;netRgName&gt; storageAccountName=&lt;saname&gt; tenantId=&lt;tenantId&gt; adminUsername=&lt;username&gt; publicKey='&lt;publicKey&gt;'

</code></pre>

<p>Si quieres entender lo que sucede, la estructura es la siguiente:</p>

<p><a href="/assets/img/cycleclouddeployment.png"><img src="/assets/img/cycleclouddeployment.png" alt="" /></a></p>

<p>El fichero <em>main.bicep</em> orquesta el resto del despliegue. Esto es así ya que necesitamos desplegar recursos tanto a nivel de suscripción como a nivel de grupo de recursos. Bicep únicamente lo permite configurando el <em>targetscope</em> a nivel de suscripción del fichero principal y luego usando módulos con  <em>scopes</em> personalizados.</p>

<p>En primer lugar, se despliegua la máquina virtual junto con sus discos, tarjeta de red e IP a nivel del grupo de recursos pasado por parámetro. Una vez que ha terminado, se asigna a nivel de subscripción el rol de <em>Contributor</em> a la identidad gestionada asociada a la VM. Finalmente, se termina la instalación desplegando de nuevo a nivel del grupo de recursos de la extensión <em>CustomScript</em> que ejecuta el script de Python para inicializar CycleCloud.</p>

<p>Si en lugar de asignar los permisos a nivel de suscripción únicamente fuera necesario asignarlo a nivel del grupo de recursos, sería posible simplificar el despliegue en un único fichero con todos los recursos en él sin necesidad de usar múltiples módulos y <em>scopes</em>.</p>

<p>Si has llegado hasta aquí, ¡felicidades!, ya conoces los principios básicos de cómo automatizar el despliegue de CycleCloud para integrarlo en tus propios scripts.</p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="azure" /><category term="compute" /><category term="hpc" /><summary type="html"><![CDATA[CycleCloud proporciona la forma más sencilla de aprovisionar en Azure clústeres de computación de alto rendimiento (High Performance Computing, HPC) basados en los planificadores más utilizados en el mercado como por ejemplo: Slurm, PBS o LSF. De forma general, CycleCloud se despliega como una máquina virtual a partir de la imagen base disponible en el Marketplace de Azure. Tras ello, para completar la instalación, es necesario seguir un asistente web de tres sencillos pasos. En la mayor parte de los casos, este despliegue manual es suficiente ya que es un proceso que se realiza una única vez. Sin embargo, en algunos casos es posible que nos interese automatizar el proceso de instalación sin necesidad de acceder a la interfaz gráfica. Especialmente aquellos clientes que emplean la estrategia de Infraestructura como Código (IaC) para desplegar múltiples entornos u entornos efímeros. Este artículo se basa en los scripts de configuración de Ansible empleados por Azure HPC On-Demand Platform y permite desplegar tu servidor de CycleCloud de forma automática. En primer lugar, se explica paso a paso la configuración necesaria para a continuación proporcionar una implementación mínima en Bicep lista para ser desplegada. Información necesaria para la configuración inicial Para tener una instalación funcional de CycleCloud es necesario proporcionar los siguientes datos: Nombre del usuario y contraseña de la cuenta de usuario inicial que tendrá el rol administrador del servidor. Clave SSH pública asociada a dicho usuario que se empleará para configurar el acceso remoto a los nodos de los clústeres. Datos de la subscripción de Azure que se utilizará para desplegar los recursos asociados a los clústeres. Configurar la cuenta de administrador CycleCloud permite modificar la configuración del servidor de forma dinámica a través de ficheros JSON. Estos ficheros, al ser colocados en la carpeta /data/config dentro del directorio de instalación de CycleCloud son automáticamente importados. Si la configuración se ha importado correctamente veremos que el nombre del fichero se habrá modificado con la extensión .json.imported. Este funcionalidad permite proporcionar los datos necesarios para automatizar la instalación. En primer lugar, para configurar la cuenta de administador crearemos un nuevo fichero que llamaremos account_data.json. El nombre es indiferente por lo que es posible escoger cualquier otro nombre. El fichero será necesario crearlo en /opt/cycle_server/config/data/. El contenido será el siguiente: [ { "AdType": "Application.Setting", "Name": "cycleserver.installation.initial_user", "Value": &lt;YourUserName&gt; }, { "AdType": "AuthenticatedUser", "Name": &lt;YourUserName&gt; "RawPassword": &lt;YourUserPassword&gt;, "Superuser": true }, { "AdType": "Credential", "CredentialType": "PublicKey", "Name": "&lt;YouUserName&gt;/public" "PublicKey": &lt;YourPublicKeyInformation&gt;, }, { "AdType": "Application.Setting", "Name": "cycleserver.installation.complete", "Value": true } ] En primer lugar se esvoge el nombre de usuario administrador de CycleCloud, su contraseña y la clave de acceso pública asociada. La última propiedad indica a CycleCloud que no muestre la pantalla de configuración inicial ya que la configuración se ha realizado correctamente. Una vez guardado el fichero, comprobaremos que su extensión es modificada a .imported indicando que los cambios se han aplicado. Configurar la subscripción de Azure Tras configurar el administrador de CycleCloud, el siguiente paso es proporcionar los datos de la suscripción de Azure que usaremos para desplegar los clústeres. Crearemos un fichero JSON que llamaremos azure_data.json. Esta vez, sin embargo, no lo haremos en dicho directorio sino en nuestra $HOME. El contenido del fichero será el siguiente: { "Environment": "public", "AzureRMUseManagedIdentity": true, "AzureResourceGroup": &lt;ResourceGroupWhereForCycleCloudResources&gt;, "AzureRMApplicationId": " ", "AzureRMApplicationSecret": " ", "AzureRMSubscriptionId": &lt;AzureSubscriptionId&gt;, "AzureRMTenantId": &lt;AzureTenantId&gt;, "DefaultAccount": true, "Location": &lt;DefaultLocation&gt;, "Name": "azure", "Provider": "azure", "ProviderId": "fd6abe95-c55e-44c8-9085-68002a27c1bb", "RMStorageAccount": &lt;ExistinStorageAccountName&gt; "RMStorageContainer": &lt;CycleCloudLockerContainerName&gt; } Es importante mencionar que CycleCloud soporta dos formas de autenticarse contra Azure: Utilizando un Service Principal (Application) y su contraseña (Secret) Utilizando una identidad gestionada por Azure. La segunda opción es la recomendada ya que toda la gestión del ciclo de vida de la identidad y de los secretos es gestionada de forma automática por Azure. En este ejemplo utilizaremos esta opción. Si por algún motivo es necesario utilizar un Service Principal, únicamente tendrás que proporcionar sus datos en las propiedades AzureRMApplicationId y AzureRMApplicationSecret, y modificar a false la propiedad AzureRMUseManagedIdentity No obstante, en cualquiera de las dos opciones es importante acordarse de asignar los permisos de RBAC dentro de nuestra suscripción para desplegar los recursos. En este ejemplo utilizaremos el rol de Contributor a nivel de la suscripción. Tras asegurarnos que los permisos están correctamente configurados será necesario ejecutar el siguiente comando. /usr/local/bin/cyclecloud account create -f $HOME/azure_data.json Tras ello, si accedemos al portal de CycleCloud con los datos del usuario administrador, podremos ver que funcionan y que en la opción de Configuración (1) &gt; Suscripciones (2), la suscripción se ha configurado correctamente (3) y está recuperando la información necesaria (4). Un ejemplo mínimo de automatización con Bicep Todo el proceso anterior ha servido para entender cuáles son los pasos necesarios para automatizar la instalación sin hacer uso de la interfaz de usuario. Sin embargo, hemos seguido realizando de forma manual cada uno de los pasos. Esto son: Crear la máquina virtual desde el MarketPlace con la imagen de referencia de CycleCloud Configurar la máquina virtual para que haga uso de las identidades gestionadas Asignar los permisos RBAC a la identidad gestionada para que tenga acceso a la suscripción de Azure Generar el fichero de configuración del usuario administrador de CycleCloud en la carpeta /data/config. Importar la configuración de Azure con el comando cyclecloud account Los tres primeros puntos es posible automatizarlos con una plantilla de Bicep. Los dos últimos podemos escribir nuestros propios scripts, o, como comentábamos al principio, aprovechar el trabajo existente de az-hop. No queremos reinventar la rueda por lo que usaremos el fichero configure.py que incorpora además validaciones extras ante posibles fallos y la inicialización de la CLI de CycleCloud. Para nuestro caso particular, únicamente será necesario modificar la línea 230 para reemplazar: --url=https://localhost/cyclecloud" por --url=https://localhost/" ya que la instalación por defecto es a nivel raíz del servidor. Si quieres ver el código, está disponible en jangelfdez/cyclecloud-bicep. Para desplegarlo en tu suscripción, únicamente necesitarás ejecutar el siguiente comando reemplazando los valores de los siguiente parámetros: location: región de Azure donde se realizará el despliegue. resourceGroupname: grupo de recursos donde se creará la máquina virtual de CycleCloud. vnetName y subnetName: datos de la red donde la máquina virtual se desplegará. vnetResourceGroupName: si vuestra red virtual está en otro grupo de recursos diferente, es necesario indicarlo con este parámetro. Si está en el mismo lo puedes omitir. storageAccountName: nombre de la cuenta de almacenamiento que CycleCloud utilizará como locker. tenantId: identificador de vuestro tenant *.onmicrosoft.com. adminUsername, adminPassword, publicKey: los datos de acceso del usuario administrador de CycleCloud que coincidirán con los de la VM en este caso. az deployment sub create --location &lt;location&gt; --template-file .\main.bicep --parameters resourceGroupName=&lt;rgName&gt; vnetName=&lt;vnetName&gt; subnetName=&lt;subnetName&gt; vnetResourceGroupName=&lt;netRgName&gt; storageAccountName=&lt;saname&gt; tenantId=&lt;tenantId&gt; adminUsername=&lt;username&gt; publicKey='&lt;publicKey&gt;' Si quieres entender lo que sucede, la estructura es la siguiente: El fichero main.bicep orquesta el resto del despliegue. Esto es así ya que necesitamos desplegar recursos tanto a nivel de suscripción como a nivel de grupo de recursos. Bicep únicamente lo permite configurando el targetscope a nivel de suscripción del fichero principal y luego usando módulos con scopes personalizados. En primer lugar, se despliegua la máquina virtual junto con sus discos, tarjeta de red e IP a nivel del grupo de recursos pasado por parámetro. Una vez que ha terminado, se asigna a nivel de subscripción el rol de Contributor a la identidad gestionada asociada a la VM. Finalmente, se termina la instalación desplegando de nuevo a nivel del grupo de recursos de la extensión CustomScript que ejecuta el script de Python para inicializar CycleCloud. Si en lugar de asignar los permisos a nivel de suscripción únicamente fuera necesario asignarlo a nivel del grupo de recursos, sería posible simplificar el despliegue en un único fichero con todos los recursos en él sin necesidad de usar múltiples módulos y scopes. Si has llegado hasta aquí, ¡felicidades!, ya conoces los principios básicos de cómo automatizar el despliegue de CycleCloud para integrarlo en tus propios scripts.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/circuits.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/circuits.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Despliega una workstation Linux para visualización 3D en Azure</title><link href="http://joseangelfernandez.es/blog/linux-workstation-3d-azure.html" rel="alternate" type="text/html" title="Despliega una workstation Linux para visualización 3D en Azure" /><published>2022-10-18T00:00:00+00:00</published><updated>2022-10-18T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/linux-workstation-3d-azure</id><content type="html" xml:base="http://joseangelfernandez.es/blog/linux-workstation-3d-azure.html"><![CDATA[<p>Azure dispone de varias opciones a la hora de desplegar una máquina virtual con soporte de GPUs para aceleración gráfica en entornos de visualización remota. Desde la serie original NV, con las NVIDIA Tesla M60, hasta la quinta generación de con la serie NVadsA10 basda en las NVIDIA A10. Esta serie es laprimera que introduce el soporte al uso de GPUs particionadas con un mínimo de 1/6 de los recursos de la GPU en la versión Standard_NV6ads_A10_v5, hasta un máximo de 2 GPUs completas por máquina virtual en las Standard_NV72ads_A10_v5. Además, esta nueva generación está basada en los últimos procesadores AMD EPYC 74F3V (Milan) con una frecuencia base de 3.2 GHz y una pico de 4.0 GHz.</p>

<p>Todo ello hace de esta serie una de las más interesantes a día de hoy para cubrir tanto las necesidades más básicas de visualización hasta las más demandantes. Si necesitas configurar un entorno Linux para ello, este artículo te guía paso a paso. La configuración está basada en CentOS 7.9 como sistema operativo, emplea la versión de los drivers 510.73 debido a los requisitos impuestos porla versión GRID 14.1, y proporciona acceso remoto a través de TurboVNC junto con VirtualGL para la aceleración 3D.</p>

<p>El URN de la imagen exacta empleada es <em>“OpenLogic:CentOS:7_9-gen2:latest”</em>. Es importante tenerlo en cuenta ya que existen múltiples variantes tanto en la versión del sistema operativo, como de la generación, como el software que lleva instalado por defecto (i.e. OpenLogic:CentOS-HPC:7_9-gen2:latest)</p>

<p>El proceso se basa en los scripts de configuración en las imágenes usadas por <a href="https://github.com/Azure/az-hop">Azure HPC On-Demand Platform</a> con drivers y versiones del software actualizadas.</p>

<h2 id="preparación-del-sistema-operativo">Preparación del sistema operativo</h2>

<p>En este primer paso actualizaremos la imagen base disponible en Azur. También será necesario instalar las cabeceras del kernel de Linux y el soporte a Dynamic Kernel Module (DKMS). Estos dos últimos son empleados por los drivers de NVIDIA para generar el módulo necesario y cargarlo sin necesidad de modificar el kernel de forma completa.</p>

<p>La versión del kernel empleada es la 3.10.0-1160.76.1</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum update <span class="nt">-y</span> 
<span class="nb">sudo </span>yum <span class="nb">install</span> <span class="nt">-y</span> kernel-devel
<span class="c"># DKMS únicamente está disponible en los repos EPEL de Fedora.</span>
<span class="nb">sudo </span>rpm <span class="nt">-Uvh</span> https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
<span class="nb">sudo </span>yum <span class="nb">install</span> <span class="nt">-y</span> dkms

<span class="nb">sudo </span>reboot
</code></pre></div></div>

<p>Este reinicio permite que el sistema operativo coja los cambios tras la actualización y evitar errores más adelante. Por ejemplo, el instalador de NVIDIA no encontrará las cabeceras del kernel correctamente de forma automática.</p>

<h2 id="instalación-de-los-drivers-nvidia-grid">Instalación de los drivers NVIDIA GRID</h2>

<p>Dado que vamos a utilizar los drivers propietarios de NVIDIA, el primer paso es evitar que el kernel cargue los drivers Nouveau de código abierto. Es posible ejecutar lo siguiente como root o editar el fichero directamente con tu editor de texto preferido (i.e. nano, vim, etc.).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt;/etc/modprobe.d/nouveau.conf
blacklist nouveau
blacklist lbm-nouveau
</span><span class="no">EOF
</span></code></pre></div></div>

<p>Tras ello, instalamos los drivers de NVIDIA GRID. Es muy importante hacer uso del instalador proporcionado directamente por Microsoft en lugar de los disponibles en la página de NVIDIA. Esta versión incluye ya el licenciamiento GRID para ser utilizado en Azure configurado. Si utilizar los drivers propios de NVIDIA tendrás que configurar un servidor de licenciamiento y adquirir las licencias correspondientes, algo que no tiene sentido al estar incluidas ya en el precio de la máquina virtual.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget <span class="nt">-O</span> NVIDIA-Linux-x86_64-grid.run https://download.microsoft.com/download/6/2/5/625e22a0-34ea-4d03-8738-a639acebc15e/NVIDIA-Linux-x86_64-510.73.08-grid-azure.run 
<span class="nb">chmod</span> +x NVIDIA-Linux-x86_64-grid.run
<span class="nb">sudo</span> ./NVIDIA-Linux-x86_64-grid.run <span class="nt">-s</span> 
</code></pre></div></div>

<p>Una vez instalado con éxito, es necesario modificar la configuración de NVIDIA GRID. Para ello utilizaremos el fichero de ejemplo proporcionado por NVIDIA:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp</span> /etc/nvidia/gridd.conf.template /etc/nvidia/gridd.conf
</code></pre></div></div>

<p>Será necesario realizar los siguientes cambios:</p>

<ul>
  <li>Comentar la sección de FeatureType ya que no es necesario en esta versión personalizada de los drivers en Azure</li>
  <li>Deshabilitar la interfaz de licenciamiento en nvidia-settings con EnableUI=FALSE ya que es gestionado automáticamente en Azure.</li>
  <li>Añadir IgnoreSP=FALSE, este último no he sido capaz de encontrar el porqué más allá de que la documentación lo pide.</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>su -
<span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt;&gt;/etc/nvidia/gridd.conf
IgnoreSP=FALSE
EnableUI=FALSE 
</span><span class="no">EOF
</span><span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'/FeatureType=0/d'</span> /etc/nvidia/gridd.conf

reboot
</code></pre></div></div>

<p>Tras reiniciar, para permitir que el kernel emplee los nuevos drivers recién instalados, podremos ver que la tarjeta está correctamente configurada.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nvidia-smi

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.73.08    Driver Version: 510.73.08    CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|<span class="o">===============================</span>+<span class="o">======================</span>+<span class="o">======================</span>|
|   0  NVIDIA A10-4Q       On   | 0000E7AB:00:00.0 Off |                    0 |
| N/A   N/A    P8    N/A /  N/A |      0MiB /  4096MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|<span class="o">=============================================================================</span>|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
</code></pre></div></div>

<h2 id="instalación-del-acceso-remoto-por-vnc-con-turbovnc-y-virtualgl">Instalación del acceso remoto por VNC con TurboVNC y VirtualGL</h2>

<p>Las imágenes de Linux del marketplace de Azure no vienen por defecto con un entorno gráfico. Es por ello que necesitaremos instalar tanto el gestor de ventanas de X.org como un entorno de escritorio. En ese caso, utilizaremos <a href="https://xfce.org/">Xfce</a> debido a su bajo consumo de recursos, ideal para un entorno de trabajo remoto en la nube.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum groupinstall <span class="nt">-y</span> <span class="s2">"X Window system"</span>
<span class="nb">sudo </span>yum groupinstall <span class="nt">-y</span> xfce
</code></pre></div></div>

<p>Una vez instalado el entorno gráfico, lo siguiente será configurar el acceso por VNC. Emplearemos <a href="https://turbovnc.org/About/Introduction">TurboVNC</a> ya que se encuentra optimizado para entornos de trabajo de vídeo y 3D. Su integración con <a href="https://virtualgl.org/About/Background">VirtualGL</a> permite disponer de una solución robusta y de alto rendimiento para este tipo de aplicaciones sobre cualquier tipo de red.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nb">install</span> <span class="nt">-y</span> https://jztkft.dl.sourceforge.net/project/turbovnc/3.0.1/turbovnc-3.0.1.x86_64.rpm

<span class="nb">sudo </span>wget <span class="nt">--no-check-certificate</span> <span class="s2">"https://virtualgl.com/pmwiki/uploads/Downloads/VirtualGL.repo"</span> <span class="nt">-O</span> /etc/yum.repos.d/VirtualGL.repo

<span class="nb">sudo </span>yum <span class="nb">install</span> <span class="nt">-y</span> VirtualGL turbojpeg xorg-x11-apps
</code></pre></div></div>

<p>A la hora de configurar VirtualGL, para que los cambios de permisos aplicados sean efectivos, es necesario parar el gestor de ventanas y descargar los módulos del kernel. Si no, el asistente de configuración te indicará que los cambios no serán efectivos hasta que lo hagas.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>service gdm stop
<span class="nb">sudo </span>rmmod nvidia_drm nvidia_modeset nvidia
<span class="nb">sudo</span> /usr/bin/vglserver_config <span class="nt">-config</span> +s +f <span class="nt">-t</span>
<span class="nb">sudo </span>service gdm start
</code></pre></div></div>

<p>Tras ello, configuramos que por defecto systemd arranque en modo gráfico y, para evitar un reinicio, lo arrancamos directamente en la sesión actual.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl set-default graphical.target
<span class="nb">sudo </span>systemctl isolate graphical.target
</code></pre></div></div>

<p>El último paso es indicar qué queremos ejecutar cuando accedamos por TurboVNC y genere un nuevo display en el servidor de las X. En nuestro caso, queremos una nueva sesión de Xfce para poder trabajar.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> <span class="nv">$HOME</span>
<span class="nb">echo</span> <span class="s2">"xfce4-session"</span> <span class="o">&gt;</span> ~/.Xclients
<span class="nb">chmod </span>a+x ~/.Xclients
</code></pre></div></div>

<p>Tras ello, solo tienes que instalar el cliente de TurboVNC en tu máquina local y conectarte a la IP o DNS asociado a tu máquina virtual desplegada en Azure. El resultado será este:</p>

<p><a href="/assets/img/turbovncxfce.png"><img src="/assets/img/turbovncxfce.png" alt="" /></a></p>

<h2 id="configuraciones-extra-recomendadas">Configuraciones extra recomendadas</h2>

<h3 id="actualización-pci-bus">Actualización PCI Bus</h3>

<p>Si reiniciamos nuestra máquina virtual o esta es redesplegada en otro host por un fallo de hardware, el identificador del bus PCI puede variar. Esto provocará que nuestro entorno gráfico no funcione correctamente al no ser posible encontrar la tarjeta gráfica.</p>

<p>Para evitarlo, es recomendable configurar este script que ajusta la configuración del BusPCI cada vez que se inicia la máquina virtual para asegurarnos de que se mantiene sincronizado.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>su -
<span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt;/etc/rc.d/rc3.d/busidupdate.sh
#!/bin/bash
BUSID=</span><span class="se">\$</span><span class="sh">(nvidia-xconfig --query-gpu-info | awk '/PCI BusID/{print </span><span class="se">\$</span><span class="sh">4}')
nvidia-xconfig --enable-all-gpus --allow-empty-initial-configuration -c /etc/X11/xorg.conf --virtual=1920x1200 --busid </span><span class="se">\$</span><span class="sh">BUSID -s
# https://virtualgl.org/Documentation/HeadlessNV
sed -i '/BusID/a</span><span class="se">\ </span><span class="sh">   Option         "HardDPMS" "false"' /etc/X11/xorg.conf
</span><span class="no">EOF
</span><span class="nb">chmod</span> +x /etc/rc.d/rc3.d/busidupdate.sh
/etc/rc.d/rc3.d/busidupdate.sh
</code></pre></div></div>

<h3 id="create-a-vglrun-alias">Create a vglrun alias</h3>

<p>A la hora de configurar la acelaración de nuestro entorno gráfico lo podemos hacer a nivel de toda la sesión o a nivel de aplicación. Empleando Xfce como entorno de escritorio no es necesario lo primero y podemos dedicar todos los recursos de la GPU para nuestras aplicaciones.</p>

<p>Para asegurarnos de que las aplicaciones hacen uso de la acelaración, es necesario ejecutarlas a través del comando <em>vglrun</em>. Para hacer el proceso más sencillo y asegurarnos de utilizar todas las GPUs disponibles en el nodo, este script genera un alias con la configuración necesaria.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>su -
<span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt;/etc/profile.d/vglrun.sh 
#!/bin/bash
ngpu=</span><span class="se">\$</span><span class="sh">(/usr/sbin/lspci | grep NVIDIA | wc -l)
alias vglrun='/usr/bin/vglrun -d :0.</span><span class="se">\$</span><span class="sh">(( </span><span class="se">\$</span><span class="sh">{port:-0} % </span><span class="se">\$</span><span class="sh">{ngpu:-1}))'
</span><span class="no">EOF
</span></code></pre></div></div>

<h3 id="incrementar-el-tamaño-de-los-buffers-de-red">Incrementar el tamaño de los buffers de red</h3>

<p>Es posible que la configuración predeterminada de red y dispositivo de red de Linux no proporcione un rendimiento (ancho de banda) y latencia óptimos para escenarios de trabajo en paralelo. Es por ello que es recomendable incrementar el tamaño de los buffers de escritura y lectura a nivel del sistema operativo.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh"> &gt;&gt;/etc/sysctl.conf
net.core.rmem_max=2097152
net.core.wmem_max=2097152
</span><span class="no">EOF
</span></code></pre></div></div>

<p>Si has llegado aquí, ¡felicidades!, ya tienes disponible tu workstation Linux para visualización 3D en Azure. El siguiente paso será instalar las aplicaciones necesarias para tu caso de uso concreto.</p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="azure" /><category term="compute" /><category term="3d" /><category term="visualization" /><summary type="html"><![CDATA[Azure dispone de varias opciones a la hora de desplegar una máquina virtual con soporte de GPUs para aceleración gráfica en entornos de visualización remota. Desde la serie original NV, con las NVIDIA Tesla M60, hasta la quinta generación de con la serie NVadsA10 basda en las NVIDIA A10. Esta serie es laprimera que introduce el soporte al uso de GPUs particionadas con un mínimo de 1/6 de los recursos de la GPU en la versión Standard_NV6ads_A10_v5, hasta un máximo de 2 GPUs completas por máquina virtual en las Standard_NV72ads_A10_v5. Además, esta nueva generación está basada en los últimos procesadores AMD EPYC 74F3V (Milan) con una frecuencia base de 3.2 GHz y una pico de 4.0 GHz. Todo ello hace de esta serie una de las más interesantes a día de hoy para cubrir tanto las necesidades más básicas de visualización hasta las más demandantes. Si necesitas configurar un entorno Linux para ello, este artículo te guía paso a paso. La configuración está basada en CentOS 7.9 como sistema operativo, emplea la versión de los drivers 510.73 debido a los requisitos impuestos porla versión GRID 14.1, y proporciona acceso remoto a través de TurboVNC junto con VirtualGL para la aceleración 3D. El URN de la imagen exacta empleada es “OpenLogic:CentOS:7_9-gen2:latest”. Es importante tenerlo en cuenta ya que existen múltiples variantes tanto en la versión del sistema operativo, como de la generación, como el software que lleva instalado por defecto (i.e. OpenLogic:CentOS-HPC:7_9-gen2:latest) El proceso se basa en los scripts de configuración en las imágenes usadas por Azure HPC On-Demand Platform con drivers y versiones del software actualizadas. Preparación del sistema operativo En este primer paso actualizaremos la imagen base disponible en Azur. También será necesario instalar las cabeceras del kernel de Linux y el soporte a Dynamic Kernel Module (DKMS). Estos dos últimos son empleados por los drivers de NVIDIA para generar el módulo necesario y cargarlo sin necesidad de modificar el kernel de forma completa. La versión del kernel empleada es la 3.10.0-1160.76.1 sudo yum update -y sudo yum install -y kernel-devel # DKMS únicamente está disponible en los repos EPEL de Fedora. sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm sudo yum install -y dkms sudo reboot Este reinicio permite que el sistema operativo coja los cambios tras la actualización y evitar errores más adelante. Por ejemplo, el instalador de NVIDIA no encontrará las cabeceras del kernel correctamente de forma automática. Instalación de los drivers NVIDIA GRID Dado que vamos a utilizar los drivers propietarios de NVIDIA, el primer paso es evitar que el kernel cargue los drivers Nouveau de código abierto. Es posible ejecutar lo siguiente como root o editar el fichero directamente con tu editor de texto preferido (i.e. nano, vim, etc.). cat &lt;&lt;EOF &gt;/etc/modprobe.d/nouveau.conf blacklist nouveau blacklist lbm-nouveau EOF Tras ello, instalamos los drivers de NVIDIA GRID. Es muy importante hacer uso del instalador proporcionado directamente por Microsoft en lugar de los disponibles en la página de NVIDIA. Esta versión incluye ya el licenciamiento GRID para ser utilizado en Azure configurado. Si utilizar los drivers propios de NVIDIA tendrás que configurar un servidor de licenciamiento y adquirir las licencias correspondientes, algo que no tiene sentido al estar incluidas ya en el precio de la máquina virtual. wget -O NVIDIA-Linux-x86_64-grid.run https://download.microsoft.com/download/6/2/5/625e22a0-34ea-4d03-8738-a639acebc15e/NVIDIA-Linux-x86_64-510.73.08-grid-azure.run chmod +x NVIDIA-Linux-x86_64-grid.run sudo ./NVIDIA-Linux-x86_64-grid.run -s Una vez instalado con éxito, es necesario modificar la configuración de NVIDIA GRID. Para ello utilizaremos el fichero de ejemplo proporcionado por NVIDIA: sudo cp /etc/nvidia/gridd.conf.template /etc/nvidia/gridd.conf Será necesario realizar los siguientes cambios: Comentar la sección de FeatureType ya que no es necesario en esta versión personalizada de los drivers en Azure Deshabilitar la interfaz de licenciamiento en nvidia-settings con EnableUI=FALSE ya que es gestionado automáticamente en Azure. Añadir IgnoreSP=FALSE, este último no he sido capaz de encontrar el porqué más allá de que la documentación lo pide. sudo su - cat &lt;&lt;EOF &gt;&gt;/etc/nvidia/gridd.conf IgnoreSP=FALSE EnableUI=FALSE EOF sed -i '/FeatureType=0/d' /etc/nvidia/gridd.conf reboot Tras reiniciar, para permitir que el kernel emplee los nuevos drivers recién instalados, podremos ver que la tarjeta está correctamente configurada. nvidia-smi +-----------------------------------------------------------------------------+ | NVIDIA-SMI 510.73.08 Driver Version: 510.73.08 CUDA Version: 11.6 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 NVIDIA A10-4Q On | 0000E7AB:00:00.0 Off | 0 | | N/A N/A P8 N/A / N/A | 0MiB / 4096MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+ Instalación del acceso remoto por VNC con TurboVNC y VirtualGL Las imágenes de Linux del marketplace de Azure no vienen por defecto con un entorno gráfico. Es por ello que necesitaremos instalar tanto el gestor de ventanas de X.org como un entorno de escritorio. En ese caso, utilizaremos Xfce debido a su bajo consumo de recursos, ideal para un entorno de trabajo remoto en la nube. sudo yum groupinstall -y "X Window system" sudo yum groupinstall -y xfce Una vez instalado el entorno gráfico, lo siguiente será configurar el acceso por VNC. Emplearemos TurboVNC ya que se encuentra optimizado para entornos de trabajo de vídeo y 3D. Su integración con VirtualGL permite disponer de una solución robusta y de alto rendimiento para este tipo de aplicaciones sobre cualquier tipo de red. sudo yum install -y https://jztkft.dl.sourceforge.net/project/turbovnc/3.0.1/turbovnc-3.0.1.x86_64.rpm sudo wget --no-check-certificate "https://virtualgl.com/pmwiki/uploads/Downloads/VirtualGL.repo" -O /etc/yum.repos.d/VirtualGL.repo sudo yum install -y VirtualGL turbojpeg xorg-x11-apps A la hora de configurar VirtualGL, para que los cambios de permisos aplicados sean efectivos, es necesario parar el gestor de ventanas y descargar los módulos del kernel. Si no, el asistente de configuración te indicará que los cambios no serán efectivos hasta que lo hagas. sudo service gdm stop sudo rmmod nvidia_drm nvidia_modeset nvidia sudo /usr/bin/vglserver_config -config +s +f -t sudo service gdm start Tras ello, configuramos que por defecto systemd arranque en modo gráfico y, para evitar un reinicio, lo arrancamos directamente en la sesión actual. sudo systemctl set-default graphical.target sudo systemctl isolate graphical.target El último paso es indicar qué queremos ejecutar cuando accedamos por TurboVNC y genere un nuevo display en el servidor de las X. En nuestro caso, queremos una nueva sesión de Xfce para poder trabajar. cd $HOME echo "xfce4-session" &gt; ~/.Xclients chmod a+x ~/.Xclients Tras ello, solo tienes que instalar el cliente de TurboVNC en tu máquina local y conectarte a la IP o DNS asociado a tu máquina virtual desplegada en Azure. El resultado será este: Configuraciones extra recomendadas Actualización PCI Bus Si reiniciamos nuestra máquina virtual o esta es redesplegada en otro host por un fallo de hardware, el identificador del bus PCI puede variar. Esto provocará que nuestro entorno gráfico no funcione correctamente al no ser posible encontrar la tarjeta gráfica. Para evitarlo, es recomendable configurar este script que ajusta la configuración del BusPCI cada vez que se inicia la máquina virtual para asegurarnos de que se mantiene sincronizado. sudo su - cat &lt;&lt;EOF &gt;/etc/rc.d/rc3.d/busidupdate.sh #!/bin/bash BUSID=\$(nvidia-xconfig --query-gpu-info | awk '/PCI BusID/{print \$4}') nvidia-xconfig --enable-all-gpus --allow-empty-initial-configuration -c /etc/X11/xorg.conf --virtual=1920x1200 --busid \$BUSID -s # https://virtualgl.org/Documentation/HeadlessNV sed -i '/BusID/a\ Option "HardDPMS" "false"' /etc/X11/xorg.conf EOF chmod +x /etc/rc.d/rc3.d/busidupdate.sh /etc/rc.d/rc3.d/busidupdate.sh Create a vglrun alias A la hora de configurar la acelaración de nuestro entorno gráfico lo podemos hacer a nivel de toda la sesión o a nivel de aplicación. Empleando Xfce como entorno de escritorio no es necesario lo primero y podemos dedicar todos los recursos de la GPU para nuestras aplicaciones. Para asegurarnos de que las aplicaciones hacen uso de la acelaración, es necesario ejecutarlas a través del comando vglrun. Para hacer el proceso más sencillo y asegurarnos de utilizar todas las GPUs disponibles en el nodo, este script genera un alias con la configuración necesaria. sudo su - cat &lt;&lt;EOF &gt;/etc/profile.d/vglrun.sh #!/bin/bash ngpu=\$(/usr/sbin/lspci | grep NVIDIA | wc -l) alias vglrun='/usr/bin/vglrun -d :0.\$(( \${port:-0} % \${ngpu:-1}))' EOF Incrementar el tamaño de los buffers de red Es posible que la configuración predeterminada de red y dispositivo de red de Linux no proporcione un rendimiento (ancho de banda) y latencia óptimos para escenarios de trabajo en paralelo. Es por ello que es recomendable incrementar el tamaño de los buffers de escritura y lectura a nivel del sistema operativo. cat &lt;&lt; EOF &gt;&gt;/etc/sysctl.conf net.core.rmem_max=2097152 net.core.wmem_max=2097152 EOF Si has llegado aquí, ¡felicidades!, ya tienes disponible tu workstation Linux para visualización 3D en Azure. El siguiente paso será instalar las aplicaciones necesarias para tu caso de uso concreto.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/sourcecode.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/sourcecode.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Opciones de salida a Internet en Azure: ¿Load Balancer o NAT Gateway?</title><link href="http://joseangelfernandez.es/blog/opciones-salida-internet-azure.html" rel="alternate" type="text/html" title="Opciones de salida a Internet en Azure: ¿Load Balancer o NAT Gateway?" /><published>2021-02-21T00:00:00+00:00</published><updated>2021-02-21T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/opciones-salida-internet-azure</id><content type="html" xml:base="http://joseangelfernandez.es/blog/opciones-salida-internet-azure.html"><![CDATA[<p>Azure proporciona por defecto conectividad de salida a Internet a cualquier máquina virtual desplegada dentro de una subred. Esto simplifica la configuración inicial cuando necesitamos que nuestras máquinas virtuales accedan a contenido fuera de Azure. Si no necesitamos nada más, con el comportamiento por defecto sería más que suficiente. Sin embargo, ¿qué sucede cuándo necesitamos tener un mayor control de nuestros flujos de salida? Por ejemplo, ¿qué podemos hacer para asegurarnos que siempre sale por la misma IP? ¿cómo podemos controlar mejor el agotamiento de los puertos para SNAT?</p>

<p>En estos escenarios tenemos dos alternativas, desplegar un <a href="https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-overview">balanceador de carga</a> o un <a href="https://docs.microsoft.com/en-us/azure/virtual-network/nat-overview">NAT Gateway</a> que nos proporcione  capacidades avanzadas. Sin embargo, ¿cuál de los dos debería escoger?</p>

<p>Si este es tu caso y no sabes qué opción es la más recomendable, la siguiente tabla resume las principales diferencias entre ambos:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Load Balancer</th>
      <th>NAT Gateway</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Configuración</strong></td>
      <td>Detallada. Compleja.</td>
      <td>Sencilla.</td>
    </tr>
    <tr>
      <td><strong>Alcance</strong></td>
      <td>Recursos en un pool de balanceo.</td>
      <td>Todos los recursos de la subred.</td>
    </tr>
    <tr>
      <td><strong>Timeout</strong></td>
      <td>4 minutos por defecto. <br /> Configurable (4-30min). <br /> Opción de TCP Reset en estado Idle.</td>
      <td>4 minutos por defecto. <br /> Configurable (4-120min). <br /> TCP Reset con paquetes inesperados.</td>
    </tr>
    <tr>
      <td><strong>Flujos de tráfico</strong></td>
      <td>Únicamente de salida.  <br /> Compatibilidad con LB o PIP</td>
      <td>Entrada y salida</td>
    </tr>
    <tr>
      <td><strong>Frontend</strong></td>
      <td>Public IP, Public IP Prefix.</td>
      <td>Public IP, Public IP Prefix.</td>
    </tr>
    <tr>
      <td><strong>Zonas de disponibilidad</strong></td>
      <td>Opcional. <br /> Redudante de zona, zonal. <br /> Stateless.</td>
      <td>Opcional. <br /> Zonal. <br /> Stateful.</td>
    </tr>
    <tr>
      <td><strong>Ancho de banda</strong></td>
      <td>Sin limitaciones tráfico de salida.</td>
      <td>50Gbps.</td>
    </tr>
    <tr>
      <td><strong>Nº IPs</strong></td>
      <td>Nº máquinas x SKU.</td>
      <td>Hasta 16 direcciones IP.</td>
    </tr>
    <tr>
      <td><strong>Nº Flujos</strong></td>
      <td>Límite de la máquina virtual.</td>
      <td>Hasta 1M de flujos concurrentes.</td>
    </tr>
    <tr>
      <td><strong>Coste</strong></td>
      <td>Instancia, reglas de balanceo y datos procesados.</td>
      <td>Instancia y datos procesados.</td>
    </tr>
  </tbody>
</table>

<p>Por regla general, si nuestras instancias van a publicar algún tipo de servicio hacia Internet la opción recomendada es utilizar un balanceador de carga; por el contrario, si únicamente son instancias internas, la solución sería un NAT Gateway. El principal problema es que los costes de la segunda solución puede ser mayores que la primera al tener un mayor coste por hora y por tráfico procesado.</p>

<p>Es por eso necesario comparar las características de ambas soluciones y escoger aquella que mejor se ajusta a nuestro escenario, no solo en funcionalidad si no también en precio.</p>

<p><em>Photo by form <a href="https://pxhere.com/es/photo/1616396">PxHere</a></em></p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="azure" /><category term="networking" /><summary type="html"><![CDATA[Azure proporciona por defecto conectividad de salida a Internet a cualquier máquina virtual desplegada dentro de una subred. Esto simplifica la configuración inicial cuando necesitamos que nuestras máquinas virtuales accedan a contenido fuera de Azure. Si no necesitamos nada más, con el comportamiento por defecto sería más que suficiente. Sin embargo, ¿qué sucede cuándo necesitamos tener un mayor control de nuestros flujos de salida? Por ejemplo, ¿qué podemos hacer para asegurarnos que siempre sale por la misma IP? ¿cómo podemos controlar mejor el agotamiento de los puertos para SNAT?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/networkcable.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/networkcable.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Regresión lineal en detalle, los primeros pasos en Machine Learning</title><link href="http://joseangelfernandez.es/blog/regresion-lineal-en-detalle-I.html" rel="alternate" type="text/html" title="Regresión lineal en detalle, los primeros pasos en Machine Learning" /><published>2020-12-27T00:00:00+00:00</published><updated>2020-12-27T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/regresion-lineal-en-detalle-I</id><content type="html" xml:base="http://joseangelfernandez.es/blog/regresion-lineal-en-detalle-I.html"><![CDATA[<p>El primer paso para cualquier persona que quiera empezar con el Aprendizaje Automático o <em>Machine Learning</em> es bastante probable que sea la regresión lineal. Si realmente su uso entra en la categoría del Aprendizaje Automático o, en su lugar, es simplemente una herramienta de modelado estadístico daría para otra conversación. Yo personalmente me decanto más por la versión del <em>Machine Learning</em> como la mezcla entre la estadística y la probabilidad, algo más allá de ese halo místico que parece envolver a todo lo relacionado con la Inteligencia Artificial. Sin embargo, visto así parece que se le quita toda la magia y la mercadotecnia.</p>

<p>Recientemente acabé el <a href="https://www.uc3m.es/master/big-data">Máster Universatrio en Métodos Análiticos para Datos Masivos: Big Data</a> de la Universidad Carlos III. Una forma de salir del día a día en el mundo de la infraestructura y el despliegue de aplicaciones en el que he estado los últimos diez años, al tan relevante nuevo mundo del dato, la piedra angular de las organizaciones para los próximos años.</p>

<p>Trabajar y estudiar no es algo sencillo y una de las cosas que me arrepiento es no poder haber disfrutado de más tiempo para bajar al detalle en los conceptos y las técnicas que nos enseñaron. Como por ejemplo, el motivo detrás de este artículo: conocer más en profundidad los detalles que hay detrás de una regresión lineal, más allá de la facilidad que algunas librerías como Scikit-learn nos proporciona con su <em>split</em>, <em>fit</em> y <em>predict</em>.</p>

<p>En este artículo la idea es conocer qué tipo de problemas son candidatos para aplicar una regresión lineal, qué condiciones tienen que cumplir los datos de los que disponemos para que realmente el resultado del modelo tenga algún valor para sacar conclusiones y las opciones matemáticas a la hora de resolverlo. Es posible que información similar aparezca por la Wikipedia u otros sitios webs; sin embargo, leerlo no implica saberlo por lo que no hay nada mejor que intentar explicarlo para ver hasta qué punto se ha comprendido.</p>

<h3 id="qué-problemas-son-candidatos-para-una-regresión-lineal">¿Qué problemas son candidatos para una regresión lineal?</h3>

<p>Los modelos de aprendizaje automático basados en el uso de una regresión lineal se emplean para predecir el valor de una variable respuesta, $Y$, a un conjunto de variables denominadas predictores, $X_i$. También se le conoce respectivamente como variable dependiente y variables independientes. Como bien indica su nombre, la relación entre ellas será una relación lineal en los parámetros y se puede expresar según la ecuación \eqref{eq:rl}.</p>

\[\begin{equation}  Y = \beta_0 + \beta_1 X + ... + \beta_p X_p + \epsilon = \beta_0 + \sum_{i=1}^p \beta_iX_i + \epsilon  \label{eq:rl}\end{equation}\]

<p>Estos parámetros $\beta_i$ serán los que tendremos que obtener a partir de los datos disponibles de nuestro problema. La linealidad de la regresión va asociado a dichos parámetros, no a las variables predictoras. Es decir, el siguiente caso también es una regresión lineal aunque pudiera parecer lo contrario ya que sus parámetros son lineales.</p>

\[Y = \beta_0 + \beta_1 \log X + ... + \beta_p X_p^2 + \epsilon\]

<p>En cuanto a \(\epsilon\), se considera una variable aleatoria que modela la interferencia de otros posibles predictores que afecten a nuestra relación lineal pero que no son considerados en nuestro modelo. Es posible verlo como el <em>error</em> de nuestra predicción respecto a la realidad si consideráramos todas las variables que afectarían a nuestro problema; en la mayor parte de los casos esas variables no incluídas ni siquiera son conocidas. Dicho error tendrá una media nula y es independiente de los predictores $X_p$.</p>

<h3 id="puedo-utilizar-una-regresión-lineal-con-los-datos-que-tengo">¿Puedo utilizar una regresión lineal con los datos que tengo?</h3>

<p>Una regresión lineal asume que los datos cumplen una serie de propiedades probabilísticas que nos permitan extraer conclusiones válidas a partir de los resultados obtenidos. Es decir, que las predicciones que realicemos con el modelo se ajusten a nuestro escenario real y tengan algún sentido.</p>

<p>Esto se debe a que los datos con los que contamos son una muestra de todos los posibles valores existentes de nuestro escenario completo. Si los datos no cumplen esas propiedades probabilísticas, no podremos extrapolar los resultados obtenidos a partir de la muestra a todo el conjunto completo de datos.</p>

<p>Las propiedades que tienen que cumplirse son: linealidad, homocedasticidad, normalidad e independencia de los errores. Pero, ¿qué implica cada una de ellas?</p>

<h4 id="linealidad">Linealidad</h4>

<p>Cuando hablamos de <em>linealidad</em>, nos referimos a que la variable que predecimos debe de mantener una relación lineal con cada una de las variables predictoras. Expresado de forma matemática, la esperanza de la variable predicha equivale a una combinación lineal de las predictoras.</p>

\[\mathbb{E}[Y | X_1 = x_1, ..., X_p = x_p] = \beta_0 + \beta_1 x_i + ... + \beta_p x_p\]

<p>Esto es así ya que como mencionabamos anteriormente, la media del error es nula</p>

\[\mathbb{E}[\epsilon |X_1 = x_1, ..., X_p = x_p ] = 0\]

<p>En el caso de una regresión lineal simple con un solo predictor, la forma más sencillo de comprobarlo es utilizando un diagrama de dispersión (<em>scatterplot</em>) en el que se apreciará la relación entre el predictor y la variable respuesta. Sin embargo, en el caso de una regresión lineal múltiple, su forma de validarlo es comparando en un diagrama similar los residuos con el valor predicho. Más detalles de lo que es un residuo se pueden encontrar más adelante.</p>

<h4 id="homocedasticidad">Homocedasticidad</h4>

<p>Probablemente de las palabras más trabalenguas que conozco para decirla bien a la primera. Sin embargo, su explicación es más sencilla. Los residuos deben de tener una varianza constante para cualquier valor de entrada. Expresado de forma matemática.</p>

\[\mathbb{Var}[\epsilon | X_1 = x_1, ..., X_p = x_p] =\sigma^2\]

<p>Es posible comprobarlo igual que antes con un diagrama de dispersión de los residuos comparado con los valores predichos. Su imcumplimiento provoca que no se pueda predecir de forma certera los errores de las predicciones que realiza nuestro modelo.</p>

<h4 id="normalidad">Normalidad</h4>

<p>A partir de lo que hemos visto en los dos puntos anteriores tenemos esta tercera condición.</p>

\[\epsilon \sim \mathcal{N}(0,\sigma^2)\]

<p>Es decir, los errores siguen una distribución normal de media nula y varianza $\sigma^2$. Si esto no sucede, afecta a la hora de calcular los intervalos de confianza y considerar las probabilidades de que el error de una predicción excede un valor particular.</p>

<h4 id="independencia-de-los-errores">Independencia de los errores</h4>

<p>Los residuos obtenidos no deben de tener ninguna correlación entre ellos. Es decir, son independientes. Generalmente esto sucede en casos en los que los datos proceden de algún tipo de serie temporal en el que un valor tiene dependencia o correlación con los anteriores o posteriores. La forma más sencillo de diagnosticarlo es a través de un gráfico con las autocorrelaciones de los residuos. La mayor parte de los valores deberían caer entorno al cero.</p>

<h3 id="resolución-analítica-o-numérica">¿Resolución analítica o numérica?</h3>

<p>En este punto disponemos ya de nuestros datos de entrada y hemos verificado que cumplen las propiedades necesarias para que los resultados de la regresión lineal tengan sentido. ¿Cómo obtenemos entonces ahora los coeficientes asociados a nuestros predictores, solución de nuestro modelo de regresión lineal?. Desde el punto de vista matemático, contamos con dos opciones para ello: la <em>resolución analítica</em> o la <em>resolución numérica</em>.</p>

<p>En el primer escenario, buscamos una ecuación o conjunto de ecuaciones que nos permitan calcular la solución del problema de regresión lineal para cualquier valor de los predictores que empleemos. De esta manera, únicamente será necesario reemplazar los valores de nuestro problema específico en el conjunto de ecuaciones y tendremos la solución. En el segundo, por el contrario, en lugar de buscar una serie de ecuaciones universales nos centraremos en obtener la solución para los valores específicos de nuestros predictores.</p>

<p>Comparando ambas opciones, parece que el camino más adecuado es el primero: obtener una solución analítica que nos permita obtener la solución en cualquier escenario que nos encontremos. Sin embargo, mientras que esto es sencillo para el escenario de una regresión lineal simple donde únicamente tenemos un predictor, la situación se complica cuando necesitamos resolver una regresión lineal múltiple en la que el número de predictores aumenta. En estos casos es posible que el coste computacional para obtener la solución sea tan alto que necesitemos resolverlo numéricamente de forma más eficiente.</p>

<p>Veamos a continuación los detalles de ambos casos:</p>

<h4 id="resolución-analítica">Resolución analítica</h4>

<p>En primer lugar obtendremos la resolución analítica para el caso más sencillo: la regresión lineal simple. Como hemos visto anteriormente en el artículo, ésta se puede representar en forma de ecuación de la siguiente manera.</p>

\[Y = \beta_0 + \beta_1 X + \epsilon\]

<p>Los datos de entrenamiento que tenemos para nuestro modelo podemos representarlos como el conjunto de tuplas $(X_1,Y_1),…,(X_n,Y_n)$ en el que para cada valor del predictor, $X_i$, conocemos el valor real de la respuesta, $Y_i$. De esta manera, necesitaremos encontrar los valores adecuados de los parámetros desconocidos $\beta_0,\beta_1$.</p>

<p>Utilizando el razonamiento geométrico, en la regresión lineal simple la solución es la línea recta que minimiza la distancia entre ella y cada una de las tuplas de entrada. Esta distancia se puede representar como la diferencia entre el valor real conocido de nuestra respuesta, $Y_i$, y el valor predicho por nuestro modelo, $\hat Y_i$.</p>

<p>La diferencia entre ambos valores, $Y_i - \hat Y_i$, se conoce como el residuo. En nuestro caso nos interesará minimizar la suma de todos los residuos obtenidos a partir de los datos de entrada. Al ser una diferencia, nos encontraremos resultados positivos y negativos que tenderán a anularse, por lo que se emplea el cuadrado de la diferencia a la hora de minimizar esa suma. Este concepto se le denomina como la suma de los residuos al cuadrado, o como se encuentra habitualmente en inglés: RSS <em>(Residual Sum of Squares)</em>.</p>

<p>Los valores reales de $(\beta_0, \beta_1)$ únicamente los podríamos conocer si tuviéramos los detalles de todas las tuplas asociadas a nuestro problema; es decir, conociéramos toda la población. Sin embargo, este nunca será el caso, únicamente tendremos una muestra de la población por lo que los valores que obtendremos serán una aproximación a ellos. Es por ese motivo que nos referiremos a ellos como $(\hat\beta_0,\hat\beta_1)$.</p>

<p>Representado de forma matemática, nuestros dos parámetros $(\hat\beta_0,\hat\beta_1)$ serán aquellos que minimicen la suma de los residuos al cuadrado de todas las tuplas que disponemos.</p>

\[(\hat\beta_0,\hat\beta_1) = \underset{(\beta_0,\beta_1)\in\Re} {\operatorname{arg\,min\,RSS}} (\beta_0,\beta_1) := \underset{(\beta_0,\beta_1)\in\Re} {\operatorname{arg\,min}}(\sum_{i=1}^n(Y_i - \hat Y_i)^2)\]

<p>Reemplazando el valor de nuestra estimación, $\hat Y_i$, por su expresión matemática, el problema quedaría planteando de la siguiente manera.</p>

\[(\hat\beta_0,\hat\beta_1) = \underset{(\beta_0,\beta_1)\in\Re} {\operatorname{arg\,min}}(\sum_{i=1}^n(Y_i - \hat\beta_0 - \hat\beta_1X_i )^2)\]

<p>Nos encontramos ante un problema de optimización en el que buscamos el valor mínimo de esa expresión. Del cálculo numérico sabemos que el mínimo o máximo de una función se da cuando la derivada de la misma es igual a $0$. Por lo tanto, el mínimo de dicha expresión para cada uno de nuestros dos parámetros $(\hat\beta_0, \hat\beta_1)$ quedará definido como:</p>

\[\frac \partial{\hat\beta_0} [\sum_{i=1}^n(Y_i - \hat\beta_0 - \hat\beta_1X_i )^2)] = 0\]

\[\frac \partial{\hat\beta_1} [\sum_{i=1}^n(Y_i - \hat\beta_0 - \hat\beta_1X_i )^2)] = 0\]

<p>Por lo tanto, el siguiente paso será resolver cada una de estas expresiones por separado. Comenzaremos primero con la derivada respecto $\beta_0$. Dado que la derivada es una operación lineal, la derivada de una suma se puede representar como la suma de sus derivadas.</p>

\[\frac \partial{\hat\beta_0} [\sum_{i=1}^n(Y_i - \hat\beta_0 - \hat\beta_1X_i )^2] =  \sum_{i=1}^n [\frac \partial{\hat\beta_0} (Y_i - \hat\beta_0 - \hat\beta_1X_i )^2]\]

<p>Para simplificar, dado que únicamente nos interesa $\hat\beta_0$, haremos la siguiente sustitución en la ecuación $C=Y_i -\hat\beta_1X_i$.</p>

\[\sum_{i=1}^n [\frac \partial{\hat\beta_0} (C - \hat\beta_0)^2)]\]

<p>Aplicando la propiedad del cuadrado de la diferencia que indica que $(a-b)^2 = a^2 - 2ab + b^2$, nuestra ecuación se transfomará de la siguiente manera.</p>

\[\sum_{i=1}^n [\frac \partial{\hat \beta_0} (C^2 -2C\hat\beta_0 + \hat\beta_0^2)]\]

<p>Derivamos cada uno de los términos de la ecuación.</p>

\[\sum_{i=1}^n [\frac \partial{\hat\beta_0} (C^2) - \frac \partial{\hat\beta_0} (2C\hat\beta_0) + \frac \partial{\hat\beta_0}(\hat\beta_0^2)] = \sum_{i=1}^n [ 0 - 2C + 2\hat\beta_0]\]

<p>Deshacemos el cambio anterior de $C=Y_i -\hat\beta_1X_i$</p>

\[-2\sum_{i=1}^n(C) + 2\sum_{i=1}^n\hat\beta_0 = -2\sum_{i=1}^n(Y_i -\hat\beta_1X_i) + 2n\hat\beta_0\]

<p>Obteniendo finalmente la siguiente expresión.</p>

\[2(n\hat\beta_0 -\sum_{i=1}^nY_i + \hat\beta_1\sum_{i=1}^nX_i)\]

<p>Una vez que llegamos a esta expresión de la derivada, nos queda por lo tanto igualar el resultado a $0$ y despejar $\hat\beta_0$.</p>

\[2(n\hat\beta_0 -\sum_{i=1}^nY_i + \hat\beta_1\sum_{i=1}^nX_i) = 0\]

\[\hat\beta_0 = \frac 1 n {\sum_{i=1}^nY_i} - \frac 1 n \hat\beta_1\sum_{i=1}^nX_i\]

<p>Siendo $\frac 1 n {\sum_{i=1}^nA_i}$ la expresión de la media aritmética, podemos reducir la expresión analítica de $\hat\beta_0$ a su versión final.</p>

\[\begin{equation} \hat\beta_0 = \bar Y - \hat\beta_1 \bar X \end{equation}\]

<p>A continuación, tendremos que realizar el mismo proceso para resolver la derivada parcial respecto a $\hat\beta_1$.</p>

\[\frac \partial{\hat\beta_1} [\sum_{i=1}^n(Y_i - \hat\beta_0 - \hat\beta_1 X_i)^2] =  \sum_{i=1}^n [\frac \partial{\hat\beta_1} (Y_i - \hat\beta_0 - \hat\beta_1X_i)^2]\]

<p>Reemplazaremos el valor anterior obtenido de $\hat\beta_0$ en la ecuación.</p>

\[\sum_{i=1}^n [\frac \partial{\hat\beta_1} (Y_i - \bar Y + \hat\beta_1 \bar X - \hat\beta_1X_i)^2] = \sum_{i=1}^n [\frac \partial{\hat\beta_1} (Y_i - \bar Y - \hat\beta_1 ( X_i - \bar X ) )^2]\]

<p>Aplicaremos de nuevo la propiedad del cuadrado de la diferencia para transformar nuestra expresión.</p>

\[\sum_{i=1}^n [\frac \partial{\hat\beta_1} ((Y_i - \bar Y)^2 - 2(Y_i - \bar Y)\hat\beta_1( X_i - \bar X ) + \hat\beta_1^2 ( X_i - \bar X )^2) ]\]

<p>Derivamos cada uno de los términos de la ecuación.</p>

\[\sum_{i=1}^n [0 - 2(Y_i - \bar Y)( X_i - \bar X ) + 2\hat\beta_1 ( X_i - \bar X )^2) ]\]

<p>El último paso es igualar el resultado de nuestra derivada parcial en $\hat\beta_1$ a $0$.</p>

\[-2\sum_{i=1}^n [(Y_i - \bar Y)( X_i - \bar X ) - \hat\beta_1 ( X_i - \bar X )^2] = 0\]

\[\sum_{i=1}^n (Y_i - \bar Y)( X_i - \bar X )  - \sum_{i=1}^n \hat\beta_1 ( X_i - \bar X )^2 = 0\]

<p>Si despejamos respecto a $\hat\beta_1$</p>

\[\hat\beta_1 =  \frac{\sum_{i=1}^n (Y_i - \bar Y)( X_i - \bar X )}{\sum_{i=1}^n ( X_i - \bar X )^2}\]

<p>Multiplicando y dividiendo por $\frac 1 n$, las expresiones del numerador y denominador coinciden con las de la $Cov(X,Y)$ y la $Var(X)$ respectivamente.</p>

\[\begin{equation} \hat\beta_1 =  \frac{\frac 1 n \sum_{i=1}^n (Y_i - \bar Y)( X_i - \bar X )}{\frac 1 n \sum_{i=1}^n ( X_i - \bar X )^2} = \frac{Cov(X,Y)}{Var(X)} \end{equation}\]

<p>Por lo tanto, para la regresión lineal simple, los dos parámetros se pueden obtener como:</p>

\[\begin{equation}
\hat\beta_0 = \bar Y - \hat\beta_1 \bar X  \\
\hat\beta_1 = \frac{Cov(X,Y)}{Var(X)}
\end{equation}\]

<p>Tras obtener el resultado para una regresión lineal, es el momento ahora de obtener la solución analítica en el caso de una regresión lineal múltiple. En este caso, en lugar de un único predictor, tendremos $n$ predictores.</p>

\[Y = \beta_0 + \beta_1 X + ... + \beta_p X_p + \epsilon = \beta_0 + \sum_{i=1}^p \beta_iX_i + \epsilon\]

<p>Los datos de entrenamiento en este caso serán $(X_{11}, X_{12}, \dots,X_{1p},Y_1), \dots ,(X_{n1}, X_{n2}, \dots,X_{np},Y_n)$, por lo que necesitaremos encontrar los valores adecuados de los parámetros desconocidos $(\beta_0, \dots, \beta_p)$.</p>

<p>Siguiendo el mismo razonamiento geométrico, en la regresión lineal múltiple la solución es la intersección de los $p$ hiperplanos definidos por cada uno de los predictores. Mientras que en tres dimensiones es interpretable gráficamente, para valores de $p$ mayores no es posible. Aún así, siguendo siendo aplicable la teoría de los residuos anterior en la que buscaremos minimizar la diferencia entre el valor real conocido de nuestra respuesta, $Y_i$, y el valor predicho por nuestro modelo, $\hat Y_i$.</p>

\[(\hat\beta_0, \dots, \hat\beta_p) = \underset{(\beta_0, \dots, \beta_p)\in\Re} {\operatorname{arg\,min\,RSS}} (\beta_0,\dots,\beta_p) := \underset{(\beta_0, \dots, \beta_p)\in\Re} {\operatorname{arg\,min}}(\sum_{i=1}^n(Y_i - \hat Y_i)^2)\]

<p>Si consideramos que $r_i = Y_i - \hat Y_i$, siendo $r_i$ el residuo para la tupla $i$.</p>

\[(\hat\beta_0, \dots, \hat\beta_p) = \underset{(\beta_0, \dots, \beta_p)\in\Re} {\operatorname{arg\,min\,RSS}} (\beta_0,\dots,\beta_p) := \underset{(\beta_0, \dots, \beta_p)\in\Re} {\operatorname{arg\,min}}(\sum_{i=1}^n r_i^2)\]

<p>Podemos comprobar que $\sum_{i=1}^n r_i^2 = r^Tr$ ya que:</p>

\[r^Tr = 
\begin{bmatrix}
r_1 &amp; r_2 &amp; \dots &amp; r_n \\
\end{bmatrix}	
\begin{bmatrix}
r_1 \\
r_2 \\
\vdots \\
r_n 
\end{bmatrix}	= \sum_{i=1}^n r_i^2\]

<p>Por lo tanto, igual que en el caso de la regresión lineal simple tendremos que minimizar la expresión respecto a $\hat\beta$. Sin embargo, no resulta práctico esta expresión a la hora de operar matemáticamente con ello. Trabajaremos en su lugar con su representación matricial.</p>

<p>Podemos representar las ecuaciones del sumatorio en $i$ de la siguiente manera.</p>

\[\begin{bmatrix}
\hat Y_1 \\
\hat Y_2 \\
\vdots \\
\hat Y_n
\end{bmatrix} =

\begin{bmatrix}
1 &amp; X_{11} &amp; X_{12} &amp; \dots &amp; X_{1p}\\
1 &amp; X_{21} &amp; X_{22} &amp; \dots &amp; X_{2p}\\
\vdots &amp; \vdots &amp; \vdots &amp; \ddots &amp; \vdots \\
1 &amp; X_{n1} &amp; X_{n2} &amp; \dots &amp; X_{np}
\end{bmatrix}
\begin{bmatrix}
\hat\beta_0 \\
\hat\beta_1 \\
\vdots \\
\hat\beta_p
\end{bmatrix} +

\begin{bmatrix}
\epsilon_1 \\
\epsilon_2 \\
\vdots \\
\epsilon_n
\end{bmatrix}\]

<p>En versión más compacta.</p>

\[\boldsymbol{\hat Y = X \hat\beta + \epsilon}\]

<p>Por lo tanto, la expresión anterior puede reescribirse en forma de matrices.</p>

\[(\hat\beta_0, \dots, \hat\beta_p) = \underset{(\beta_0, \dots, \beta_p)\in\Re} {\operatorname{arg\,min}}( \boldsymbol{(Y_i - \hat Y_i)^T(Y_i - \hat Y_i)})\]

\[\frac \partial{\hat\beta} [\boldsymbol{(Y_i - \hat Y_i)^T(Y_i - \hat Y_i)}] = \frac \partial{\hat\beta} [\boldsymbol{(Y_i - X\hat\beta)^T(Y_i - X\hat\beta)}] = 0\]

<p>Aplicando cálculo matricial desarrollaremos la expresión a derivar.</p>

\[\boldsymbol{(Y_i - X\hat\beta)^T(Y_i - X\hat\beta)=(Y_i^T - \hat\beta^TX^T)(Y_i - X\hat\beta)=}\]

\[\boldsymbol{Y_i^TY_i - Y_i^TX\hat\beta - \hat\beta^TX^TY_i + \hat\beta^TX^TX\hat\beta}\]

<p>Teniendo en cuenta que $\boldsymbol{(Y_i^TX\hat\beta) = (\hat\beta^TX^TY_i)^T}$ y que su dimensión es igual a $(1 \times n)(n \times  p)(p\times1) = 1$, llegamos a la conclusión de que el valor de la matriz y su traspuesta es el mismo por lo que entonces se pueden suman entre sí.</p>

\[\boldsymbol{Y_i^TY_i - 2\hat\beta^TX^TY_i + \hat\beta^TX^TX\hat\beta}\]

<p>Finalmente.</p>

\[\frac \partial{\hat\beta} [\boldsymbol{Y_i^TY_i - 2\hat\beta^TX^TY_i + \hat\beta^TX^TX\hat\beta}] = \boldsymbol{0 - 2X^TY_i + 2X^TX\hat\beta} = 0\]

<p>Asumiendo que la matrix $X$ es invertible, podemos calcular los parámetros $\hat\beta$ de la siguiente manera.</p>

\[\begin{equation} \boldsymbol{\hat\beta = (X^TX)^{-1}X^TY_i}  \label{eq:betamlr} \end{equation}\]

<p>El cálculo de la matriz inversa para matrices de pequeña dimensión es fácil de calcular si cumple las propiedades necesarias para ser invertible. Sin embargo, cuando la dimensión de la matriz aumenta hasta miles, cientos de miles o millones de filas y columnas esta aproximación no es tan sencilla. Es por eso que es muchas veces más fácil resolve el sistema de ecuaciones $\boldsymbol{Ax=b}$ que hacer el cálculo de la inversa.</p>

<p>No entraré en más detalles al respecto pero sí que os recomiendo leer el artículo <a href="http://gregorygundersen.com/blog/2020/12/09/matrix-inversion/">“Why Shouldn’t I Invert That Matrix?” de Gregory Gundersen </a> y las referencias que se incluyen al artículo <a href="https://www.johndcook.com/blog/2010/01/19/dont-invert-that-matrix/">“Don’t invert that matrix”</a>, al artículo <a href="https://www.r-bloggers.com/2015/07/dont-invert-that-matrix-why-and-how/">“Don’t invert that matrix” – why and how”</a> o a esta conversación en <a href="https://news.ycombinator.com/item?id=11681893">Hacker News</a>.</p>

<h4 id="resolución-numérica">Resolución numérica</h4>

<p>Dado que la aproximación analítica prácticamente es solo útil en el caso de la regresión lineal simple, es necesario buscar una alternativa para problemas de regresión lineal más complejos. Partiendo de la expresión $\eqref{eq:betamlr}$ que obtuvimos en el apartado anterior, podemos representarla en la forma general $\boldsymbol{Ax=b}$. De esta manera, nuestro problema quedaría enunciado de la siguiente manera.</p>

\[\boldsymbol{(X^TX)\hat\beta = X^TY_i}\]

<p>Existen diferentes técnicas a la hora de resolver este sistema de ecuaciones. No entraremos en detalle de todas ellas en este artículo ya que nos saldríamos del objetivo principal y entraríamos en el territorio del álgebra lineal. Sin embargo, es interesante hacer una revisión de cómo lo resuelve uno de los frameworks más utilizados en el área del aprendizaje automático: <a href="https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html#sklearn.linear_model.LinearRegression">Scikit-learn</a>.</p>

<p>En este caso concreto, el modelo que expone Scikit-learn envuelve el método de cálculo del problema <em>Ordinary Least Squares</em> dentro del paquete de álgebra lineal de <a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.lstsq.html">SciPy</a> (scipy.linalg.lstsq). Si revisamos la documentación de SciPy de éste método, podemos ver que su objetivo es calcular la solución de mínimos cuadrados de la ecuación $\boldsymbol{Ax=b}$ obteniendo el vector $x$ y minimizando su norma euclídea, $||b-Ax||_2$.</p>

<p>Para realizar esta minimización, este método de SciPy recubre a su vez la implementación en Fortran 90 de la librería <a href="http://performance.netlib.org/lapack/">LAPACK</a> para <a href="https://www.netlib.org/lapack/lug/node27.html#tabdrivellsq">problemas líneales de mínimos cuadradros</a>. El método <em>scipy.linalg.lstsq</em> permite escoger entre tres posibles opciones a la hora de realizar el cálculo:</p>

<ul>
  <li><strong><em><a href="http://www.netlib.org/lapack/explore-html/d7/d3b/group__double_g_esolve_gaa6ed601d0622edcecb90de08d7a218ec.html#gaa6ed601d0622edcecb90de08d7a218ec">gelss</a></em></strong>: emplea la descomposición en valores singulares de la matriz $A$.</li>
  <li><strong><em><a href="http://www.netlib.org/lapack/explore-html/d7/d3b/group__double_g_esolve_ga94bd4a63a6dacf523e25ff617719f752.html#ga94bd4a63a6dacf523e25ff617719f752">gelsd</a></em></strong>: emplea también la descomposición en valores singulares de la matriz $A$ pero en este caso con un algoritmo de divide y vencerás.</li>
  <li><strong><em><a href="http://www.netlib.org/lapack/explore-html/d7/d3b/group__double_g_esolve_ga385713b8bcdf85663ff9a45926fac423.html#ga385713b8bcdf85663ff9a45926fac423">gelsy</a></em></strong>: emplea una factorización ortogonal completa de la matriz $A$</li>
</ul>

<p>Si queréis seguir profundizando más en los detalles específicos os recomiendo revisar cada uno de los artículos de la documentación enlazados anteriormente. Por cada función incluyen una breve introducción donde explican a alto nivel la implementación. Si no es suficiente y existe alguna duda, el siguiente paso sería revisar un buen libro de álgebra lineal.</p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="machine learning" /><category term="regresion lineal" /><summary type="html"><![CDATA[El primer paso para cualquier persona que quiera empezar con el Aprendizaje Automático o Machine Learning es bastante probable que sea la regresión lineal. Si realmente su uso entra en la categoría del Aprendizaje Automático o, en su lugar, es simplemente una herramienta de modelado estadístico daría para otra conversación. Yo personalmente me decanto más por la versión del Machine Learning como la mezcla entre la estadística y la probabilidad, algo más allá de ese halo místico que parece envolver a todo lo relacionado con la Inteligencia Artificial. Sin embargo, visto así parece que se le quita toda la magia y la mercadotecnia.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/machinelearning.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/machinelearning.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Las opciones de almacenamiento en Azure de un vistazo</title><link href="http://joseangelfernandez.es/blog/azure-storage-vistazo.html" rel="alternate" type="text/html" title="Las opciones de almacenamiento en Azure de un vistazo" /><published>2020-12-13T00:00:00+00:00</published><updated>2020-12-13T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/azure-storage-vistazo</id><content type="html" xml:base="http://joseangelfernandez.es/blog/azure-storage-vistazo.html"><![CDATA[<p>Empiezo a pensar que tengo algo de apego especial por los servicios de almacenamiento de Azure. Intentando refrescar en mi mente todos los servicios y funcionalidades nuevas que se han incluido en el último año relacionados con el almacenamiento, me he dado cuenta que esta idea era recurrente. Justo en <a href="/sessions/2018-04-27-azure-bootcamp.html">la Azure Bootcamp de 2018 presentamos una sesión</a> <a href="http://twitter.com/iriaq">Iria</a> y yo sobre el almacenamiento en Azure.</p>

<p>Revisando los detalles de la misma veo que la descripción viene completamente a cuento de este artículo:</p>

<blockquote>
  <p>Storage es uno de los primeros servicios disponibles en Azure desde su lanzamiento y muchas veces uno de los menos conocidos. En los últimos meses se ha incorporado cada vez más funcionalidades creando un cierto caos entre qué se puede hacer con cada tipo de almacenamiento y qué no. En esta sesión pondremos un poco de luz sobre lío y sentar las bases para optimizar tu consumo de almacenamiento en Azure.</p>
</blockquote>

<p>Han pasado dos años y la sensación vuelve a ser la misma. Una serie de servicios que en un principio consideraríamos que son “simples” ya que deberían servir para almacenar nuestros datos; pero que sin embargo, con el paso del tiempo, esa simpleza se complica al incluirse nuevas características y funcionalidades que se entrecruzan.</p>

<p>Tirando de la documentación de Azure junto con <a href="https://www.linkedin.com/posts/jangelfdez_aunque-vivamos-en-un-mundo-digital-todav%C3%ADa-activity-6737445504919203840-gnQw">un poco de papel y boli</a> he tomado algunas notas y dibujado unos esquemas para que con un vistazo rápido pudiera refrescarlo en cualquier momento. Algo frecuente ya que cada vez resulta más complicado retener todo en la cabeza debido al ritmo de crecimiento de Azure.</p>

<p>Dado que es posible que a alguien más le pueda resultar útil, he consolidado esos esquemas en una única imagen que tenéis a continuación. Podéis abrirla a tamaño original si hacéis click en ella para ver mejor los detalles.</p>

<p><a href="/assets/img/azurestorageservices.png"><img src="/assets/img/azurestorageservices.png" alt="" /></a></p>

<p>El servicio más destacado, como era de esperar, es <em>Azure Storage</em> y la gran variedad de tipos de almacenamiento que incluye en él. Destaca especialmente <em>Azure Blob Storage</em> y , dentro de él, el servicio de <em>Azure Blob Block Storage</em> como base para otros servicios tan relevantes como <em>Azure Data Lake Gen 2</em>.</p>

<p>Alrededor de de <em>Azure Storage</em>, podemos encontrar los servicios para ingesta y exportación de datos de la familia <em>Data Box</em>, los sistemas de caché de alto rendimiento para entornos HPC con la familia de servicios basados en Avere y los servicios de copia de seguridad y protección frente a desastres con los <em>Recovery Services</em>.</p>

<p>Además de los propios servicios de Azure también he añadido los detalles de las capas de rendimiento y de acceso que soporta cada uno de ellos ya que dependiendo de lo que escojamos, quedarán definidos sus niveles de rendimiento y los límites máximos.</p>

<p>Espero que os sea útil.</p>

<p><em>Photo by form <a href="https://pxhere.com/en/photo/640737">PxHere</a></em></p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="azure" /><category term="storage" /><summary type="html"><![CDATA[Empiezo a pensar que tengo algo de apego especial por los servicios de almacenamiento de Azure. Intentando refrescar en mi mente todos los servicios y funcionalidades nuevas que se han incluido en el último año relacionados con el almacenamiento, me he dado cuenta que esta idea era recurrente. Justo en la Azure Bootcamp de 2018 presentamos una sesión Iria y yo sobre el almacenamiento en Azure.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/olddocuments.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/olddocuments.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Evitar el throttling listando ficheros de Azure File Shares desde la CLI</title><link href="http://joseangelfernandez.es/blog/evitar-throttling-az-file-list.html" rel="alternate" type="text/html" title="Evitar el throttling listando ficheros de Azure File Shares desde la CLI" /><published>2020-10-04T00:00:00+00:00</published><updated>2020-10-04T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/evitar-throttling-az-file-list</id><content type="html" xml:base="http://joseangelfernandez.es/blog/evitar-throttling-az-file-list.html"><![CDATA[<p>A la hora de trabajar con Azure Files es posible que tengamos la necesidad de listar los ficheros que se encuentran alojados dentro del servicio. Desde la CLI podemos realizarlo de forma sencilla a través de un comando similar al siguiente:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az storage file list <span class="nt">--share-name</span> MyShare <span class="nt">--account-name</span> MyStorageAccountName 
</code></pre></div></div>

<p>Si es algo puntual, es posible que no te encuentres ningún problema; sin embargo, si lo tenéis integrado en algún script que hace varias consultas en un corto periodo de tiempo os podéis encontrar con la limitación de ARM de realizar un <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#storage-resource-provider-limits">máximo de operaciones de tipo <em>List</em> de 100 cada 5 minutos</a>.</p>

<p>¿Por qué salta esta limitación si únicamente tendría que estar leyendo datos de la API de Azure a través del método GET correspondiente? Como siempre, todo tiene su motivo.</p>

<p>La razón es que si no se incluyen los detalles de autenticación de la cuenta de almacenamiento a la que estamos accediendo, el comando automáticamente lanza una operación para listar sus claves de acceso y autenticar la petición.</p>

<p>Si queremos evitar este problema, únicamente será necesario utilizar alguna de las opciones alternativas disponibles con el mismo comando para proporcionarle esos datos:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Empleando la clave de la cuenta</span>
az storage file list <span class="nt">--share-name</span> MyShare <span class="nt">--account-name</span> MyStorageAccountName  –account-key <span class="o">[</span>your-key]

<span class="c"># Empleando la cadena de conexión completa</span>
az storage file list <span class="nt">--share-name</span> MyShare –connection-string <span class="o">[</span>your-connection-string]

<span class="c"># Empleando un token SAS específico</span>
az storage file list <span class="nt">--share-name</span> MyShare <span class="nt">--account-name</span> MyStorageAccountName f –sas-token <span class="o">[</span>your-sas]
</code></pre></div></div>

<p>De esta manera, la CLI no necesitará ejecutar esas consultas extras y se autenticará directamente evitando así la limitación de la API.</p>

<p><em>Photo by form <a href="https://pxhere.com/en/photo/536212">PxHere</a></em></p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="azure" /><category term="storage" /><category term="files" /><summary type="html"><![CDATA[A la hora de trabajar con Azure Files es posible que tengamos la necesidad de listar los ficheros que se encuentran alojados dentro del servicio. Desde la CLI podemos realizarlo de forma sencilla a través de un comando similar al siguiente:]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/storagefiles.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/storagefiles.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Opciones para clonar un proyecto de Custom Vision</title><link href="http://joseangelfernandez.es/blog/opciones-clonar-custom-vision.html" rel="alternate" type="text/html" title="Opciones para clonar un proyecto de Custom Vision" /><published>2020-09-13T00:00:00+00:00</published><updated>2020-09-13T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/opciones-clonar-custom-vision</id><content type="html" xml:base="http://joseangelfernandez.es/blog/opciones-clonar-custom-vision.html"><![CDATA[<p>Como comentaba en un <a href="http://joseangelfernandez.es/blog/opciones-almacenamiento-custom-vision.html">artículo previo sobre Custom Vision</a>: <em>“Los <a href="https://azure.microsoft.com/es-es/services/cognitive-services/">servicio cognitivos de Microsoft</a> facilitan la incorporación de inteligencia artificial de forma sencilla en nuestros proyectos. Uno de estos servicios es el de <a href="https://azure.microsoft.com/en-us/services/cognitive-services/custom-vision-service/">*Custom Vision*</a>, con él es posible construir clasificadores o detectores de objetos a partir de un número reducido de imágenes para su entrenamiento inicial.</em></p>

<p>Sin embargo, podemos encontrarnos situaciones en las que no queramos tener únicamente un modelo de Custom Vision sino que nos interese a partir de un conjunto de imágenes base poder generar más de un modelo. De esta manera, se puede afinar cada uno ellos para un aspecto concreto empleando como base de entrenamiento el mismo conjunto de datos etiquetados.</p>

<p>Es decir, imaginemos que tenemos un proyecto A con varios cientos de imágenes clasificadas con varias decenas de objetos ya etiquetados, ¿qué puedo hacer si tengo un proyecto B en el que necesitamos un subconjunto de estos objetos ya clasificados en esos cientos de imágenes?</p>

<p>Lo ideal sería que existiera un botón que permitiera clonar automáticamente un proyecto de <em>Custom Vision</em> en uno nuevo manteniendo todo el conjunto de datos y listo para empezar a ser entrenado; sin embargo, a día de hoy no es una opción que se encuentre disponible ni en el portal ni a través de los SDKs. Para habilitar esta opción de clonación sería necesario emplear las APIs REST del servicio de Custom Vision para extraer esa información y volver a cargarla de nuevo.</p>

<p>El <a href="http://joseangelfernandez.es/blog/opciones-almacenamiento-custom-vision.html">artículo anterior</a> proporcionaba una guía sobre los métodos concretos de la API a usar y cómo invocarlos. Sin embargo, implica realizar un desarrollo a medida para el proceso iterativo de descargar todos los datos y volver a ingestarlos. No os preocupéis, alguien pasó por lo mismo anteriormente a vosotros y en el <a href="https://github.com/Azure-Samples">repositorio de GitHub de ejemplos de Azure</a> podéis encontrar <a href="https://github.com/Azure-Samples/custom-vision-move-project">“Custom Vision Move Project”</a>, una solución automatizada basada en el SDK de Python para exportar toda la información de un proyecto existente y generar una copia exacta en otro nuevo listo para empezar a entrenarlo.</p>

<p>El código es bastante claro y sencillo de leer por si tenéis algún miedo de que borre los datos o modifique la información. Básicamente es <a href="https://github.com/Azure-Samples/custom-vision-move-project/blob/master/migrate_project.py">un único fichero</a> que genera dos clientes de <em>Custom Vision</em>, origen y destino, y se encarga de leer del primero los datos para escribirlos en el segundo. Algo sencillo de implementar por uno mismo pero que siempre viene bien que alguien lo haya hecho con anterioridad. Así puedes centrarte en lo de verdad importante para tu proyecto y no perder tiempo en la preparación de la infraestructura o elementos necesarios ;)</p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="azure" /><category term="custom vision" /><category term="cognitive services" /><summary type="html"><![CDATA[Como comentaba en un artículo previo sobre Custom Vision: “Los servicio cognitivos de Microsoft facilitan la incorporación de inteligencia artificial de forma sencilla en nuestros proyectos. Uno de estos servicios es el de *Custom Vision*, con él es posible construir clasificadores o detectores de objetos a partir de un número reducido de imágenes para su entrenamiento inicial.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/listeria.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/listeria.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Leído: ‘The Unicorn Project’ de Gene Kim</title><link href="http://joseangelfernandez.es/blog/unicorn-project.html" rel="alternate" type="text/html" title="Leído: ‘The Unicorn Project’ de Gene Kim" /><published>2020-08-18T00:00:00+00:00</published><updated>2020-08-18T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/unicorn-project</id><content type="html" xml:base="http://joseangelfernandez.es/blog/unicorn-project.html"><![CDATA[<p>Si disfrutaste con la lectura de <a href="https://itrevolution.com/book/the-phoenix-project/"><em>The Phoenix Project</em></a>, encontrarás en este libro una cierta continuidad que te resultará interesante. Si no, encontrarás un relato que te conocido y que es común a las empresas de hoy en día que se encuentren en busca de la tan ansiada transformación digital. Una historia en la que probablemente te encuentres identificado si trabajas en el sector: la lucha entre negocio e IT para dar respuestas a las necesidades de la organización, habitualmente, de forma insuficiente.</p>

<p>Si en el primer libro la aventura se centraba en los retos de Bill Palmer, manager de IT en Parts Unlimited que es promocionado a Vicepresidente de Operaciones y que gracias a la aplicación de las metodologías ágiles y de DevOps consigue salvar el futuro de su compañía en el último momento. En este segundo libro la aventura se ve desde los ojos de Maxine Chambers, arquitecta de software y desarrolladora senior. Un mismo reto, un nuevo proyecto y esta vez enfocado desde el punto de vista del mundo del desarrollo en lugar de IT.</p>

<p>Me ha resultado interesante la mezcla entre la parte didáctica que intenta proporcionar el libro sobre los <a href="https://itrevolution.com/five-ideals-of-devops/">Cinco Ideales de DevOps</a>, con la parte épica de cómo un equipo que parte desde cero consigue darle la vuelta a la tortilla al más puro estilo de Los Goonies. Tan épica que probablemente sea imposible en los tiempos que mencionan, pero que realidad no estropee una buena historia.</p>

<p>Si tenéis tiempo y queréis pasar un fin de semana tranquilo de lectura la recomiendo. Sin embargo, ¡cuidado!, es probable que después de leer el mundo idealista que consiguen generar desde cero para salvar a Parts Unlimited de la quiebra, te frustres al volver a tu realidad de hacer despliegues copiando ficheros a mano al servidor de producción.</p>

<p>Avisado quedas.</p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="libros" /><summary type="html"><![CDATA[Si disfrutaste con la lectura de The Phoenix Project, encontrarás en este libro una cierta continuidad que te resultará interesante. Si no, encontrarás un relato que te conocido y que es común a las empresas de hoy en día que se encuentren en busca de la tan ansiada transformación digital. Una historia en la que probablemente te encuentres identificado si trabajas en el sector: la lucha entre negocio e IT para dar respuestas a las necesidades de la organización, habitualmente, de forma insuficiente.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/unicorn.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/unicorn.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Leído: ‘Weapons of Math Destruction’ de Cathy O’Neil</title><link href="http://joseangelfernandez.es/blog/weapons-of-math-destruction.html" rel="alternate" type="text/html" title="Leído: ‘Weapons of Math Destruction’ de Cathy O’Neil" /><published>2020-08-14T00:00:00+00:00</published><updated>2020-08-14T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/weapons-of-math-destruction</id><content type="html" xml:base="http://joseangelfernandez.es/blog/weapons-of-math-destruction.html"><![CDATA[<p>La tecnología evoluciona de forma tan rápida que cada vez resulta más complicado llegar a conocer los detalles de todo lo que se encuentra por debajo de cosas cotidianas que hacemos; por ejemplo, realizar una llamada desde nuestro teléfono móvil o arrancar el coche por la mañana para ir a trabajar. Este proceso de estratificación hace que de forma general nos quedemos en la cara más visible de la tecnología <em>(i.e. darle a llamar en nuestro teléfono o girar la llave para arrancar el coche)</em> y solo si somos algo curiosos, nos adentremos en las capas inferiores para entender lo que sucede.</p>

<p>Este desconocimiento, desde mi punto de vista, llega a convertirnos en vulnerables. Quedamos a merced de terceros cuando algo falla, no funciona como se espera o posiblemente peor, cuando funciona correctamente pero no somos conscientes de lo que está sucediendo por debajo. ¿Os suenan las cookies y la trazabilidad de la gente a través de internet? ¿La descarga de aplicaciones gratuitas y las “sorpresas” que muchas veces vienen dentro?. Si trabajas o estás interesado en este sector probablemente sea así, ¿pero tus familiares y amigos son conscientes?</p>

<p>Después de esto diréis, ¿qué tiene que ver lo anterior con este libro? Bien, la relación se encuentra en el enfoque del libro sobre uno de los aspectos de la tecnología que nos encontramos en el día a día y del que no somos conscientes del impacto que tiene en nuestra vida: los algoritmos y el Big Data.</p>

<p>Para muchas personas de nuestro alrededor, la mayor interacción consciente con sistemas “inteligentes” son las preguntas que realizan a su asistente en el móvil para conocer el tiempo o las recomendaciones que recibe de Netflix, Spotify o Amazon sobre el contenido que consumir a continuación. Experiencias que en algunos casos es posible que les hagan cuestionarse hasta qué punto de verdad existe “inteligencia” en ellos, si no, ¿cómo es posible que Amazon te recomiende de nuevo el mismo producto que ya has comprado?.</p>

<p>Sin embargo, existe un gran número de algoritmos, más sútiles e incluso impercetibles, que tienen un impacto mayor en nuestro día a día sin que seamos conscientes de ellos. Estos algoritmos son los que Cathy O’Neil denomina como <em>“weapons of math destruction”</em>:  una colección de modelos opacos, no controlados por ningún tipo de regulación, difíciles de corregir si se equivocan con nosotros y con una escalabilidad tan alta que afectan a un gran número de personas.</p>

<p>Estos algoritmos se encuentran en muchos de nuestros escenarios cotidianos. Cathy escoge situaciones como la búsqueda de trabajo, la obtención de préstamos, la contratación de seguros médicos privados o los sistemas de la policía para prevenir la delicuencia y crímenes. Sí que es cierto que son muy particulares del mercado estadounidense, por lo que en algunos casos resulta complicado seguirlos en detalle al no conocerlos de primera mano. Aún así, es fácil seguir el hilo y entender cómo estos modelos, si no son diseñados correctamente, pueden llegar a ser armas peligrosas que condicionen el futuro de los ciudadanos desde que son prácticamente niños.</p>

<p>Un matiz relevante es que en los casos concretos del libro siempre salen perjudicados los mismos: aquellos pertenecientes a clases bajas, con una educación limitada y con recursos económicos reducidos. Algo normal porque al final estos modelos recogen los mismos prejuicios de las personas que los diseñaron pero camuflados con una capa de neutralidad al estar basado en un modelo matemático. Los números no mienten, ¿no?</p>

<p>Aunque tras acabar de leer el libro el panorama puede parecer algo desolador, sí que es cierto que existen movimientos para lograr evitar muchos de los problemas que menciona como el impuso de <a href="https://es.wikipedia.org/wiki/Inteligencia_artificial_explicable"><em>Explanable AI (xAI)</em></a> o <a href="https://www.microsoft.com/en-us/ai/responsible-ai"><em>Responsible AI</em></a>. Avances que nos permitan tener esa visión tan <em>anti Big Data</em> o <em>anti IA</em> como la que puede llegar a extraerse de él.</p>

<p>No obstante, es una lectura interesante para ser consciente de a dónde nos puede dirigir este mundo liderado por modelos de aprendizaje automático si no se tienen en cuenta unos principios básicos de responsabilidad por parte de las personas que los crean.</p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="libros" /><summary type="html"><![CDATA[La tecnología evoluciona de forma tan rápida que cada vez resulta más complicado llegar a conocer los detalles de todo lo que se encuentra por debajo de cosas cotidianas que hacemos; por ejemplo, realizar una llamada desde nuestro teléfono móvil o arrancar el coche por la mañana para ir a trabajar. Este proceso de estratificación hace que de forma general nos quedemos en la cara más visible de la tecnología (i.e. darle a llamar en nuestro teléfono o girar la llave para arrancar el coche) y solo si somos algo curiosos, nos adentremos en las capas inferiores para entender lo que sucede.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/weapons.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/weapons.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Gestión de registros A en Azure DNS con el mínimo privilegio posible</title><link href="http://joseangelfernandez.es/blog/gestion-registros-azure-dns-minimo-privilegio.html" rel="alternate" type="text/html" title="Gestión de registros A en Azure DNS con el mínimo privilegio posible" /><published>2020-08-03T00:00:00+00:00</published><updated>2020-08-03T00:00:00+00:00</updated><id>http://joseangelfernandez.es/blog/gestion-registros-azure-dns-minimo-privilegio</id><content type="html" xml:base="http://joseangelfernandez.es/blog/gestion-registros-azure-dns-minimo-privilegio.html"><![CDATA[<p><em>Azure Role Based Access Control</em> proporciona un gran número de roles por defecto para gestionar nuestros recursos en Azure. Sin embargo, en algunas ocasiones es posible que éstos sean demasiado amplios para lo que nos interesa y necesesitemos limitar su alcance. Para ello, será necesario que hagamos uso de los roles personalizados.</p>

<p>En esta situación me he encontrado hoy cuando únicamente se deseaba permitir la gestión de los registros de tipo A dentro de una zona de Azure DNS y nada más. Como estoy seguro que en el futuro lo volveré a necesitar, qué mejor que dejarlo registrado aquí:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">  </span><span class="nl">"permissions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
            </span><span class="p">{</span><span class="w">
                </span><span class="nl">"actions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                    </span><span class="s2">"Microsoft.Network/privateDnsZones/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.Network/privateDnsZones/write"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.Network/privateDnsZones/A/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.Network/privateDnsZones/A/write"</span><span class="w">
                </span><span class="p">],</span><span class="w">
                </span><span class="nl">"notActions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                    </span><span class="s2">"Microsoft.Network/privateDnsZones/A/delete"</span><span class="w">
                </span><span class="p">],</span><span class="w">
                </span><span class="nl">"dataActions"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
                </span><span class="nl">"notDataActions"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">

</span></code></pre></div></div>

<p>El proceso de saber qué acciones son las que necesitamos activar o cuáles bloquear, no era una tarea fácil. Sin embargo, si aún no lo habéis probado os recomiendo que le echés un ojo al <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/custom-roles-portal#step-3-basics">editor de roles personalizados disponible en el portal</a>.</p>

<p>Si aún así preferís la experiencia dura, ya no es necesario consultar a través del CLI las operaciones que un proveedor de recursos tiene habilitadas, ahora se encuentran <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/resource-provider-operations">incluídas en la documentación</a>. ¡Ojo! Creo que probablemente sea la página más larga de las que he visto en la documentación oficial :)</p>

<p><em>Photo by form <a href="https://pxhere.com/en/photo/816636">PxHere</a></em></p>]]></content><author><name>José Ángel Fernández</name></author><category term="blog" /><category term="azure" /><category term="dns" /><summary type="html"><![CDATA[Azure Role Based Access Control proporciona un gran número de roles por defecto para gestionar nuestros recursos en Azure. Sin embargo, en algunas ocasiones es posible que éstos sean demasiado amplios para lo que nos interesa y necesesitemos limitar su alcance. Para ello, será necesario que hagamos uso de los roles personalizados.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://joseangelfernandez.es/lock.jfif" /><media:content medium="image" url="http://joseangelfernandez.es/lock.jfif" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>