Mes: septiembre 2012

Marketing recipes: Orientación al marketing (o cliente)

Después de deliberar durante algunos días y a pesar de que no tengo todo el tiempo que quisiera para mantener el blog actualizado, he decidido incorporar una nueva rama a las ya variopintas tratadas en el blog (programación web y móvil, .net, productividad, IA…), la rama de marketing. El principal motivo es que soy egoísta. Como buen cuaderno de notas técnicas (o de ingeniería) muchas veces uso el blog como sistema para reforzar conocimiento adquirido. Aunque realmente, el principal motivo es que como es fundamental tener algunos conceptos de Marketing si queremos de verdad inventarnos nuestro trabajo (un emprendedor debería ser un ingeniero del trabajo 😉 ) .
Entrando un poco en materia, hoy vamos a ver un concepto que curiosamente razoné sin haber sabido de su existencia hace no mucho tiempo atrás, y es el concepto de Orientación al Marketing (o al cliente).

¿Qué fue primero, el producto o la necesidad?

 

Para entender mejor la Orientación al Marketing, me gustaría definir primero la orientación al producto: “una empresa orientada al producto es aquella que centra sus esfuerzos en mejorar sus productos” ¿Os suena? ¡Claro! Es lo que hacemos los desarrolladores, centrarnos principalmente, en mejorar nuestros desarrollos/productos. Esto es, primero el producto y luego el cliente, creo que prácticamente todo el mundo que he conocido que ha lanzado productos al mercado, lo ha hecho así, eligiendo el producto y en base a su producto a los clientes. Si eres desarrollador para una gran empresa (o pequeña) esto seguramente no te diga nada y tu enfoque es el adecuado. Sin embargo si lo que buscas es un hueco en el mercado, en un nicho, esto es lo mismo que ir a jugar al bingo. ¡A ver si tenemos suerte y sale nuestro cartón! Creo que es evidente que este enfoque es contraproducente.

Orientación al cliente

Una empresa orientada al marketing es aquella que tiene en cuenta a la hora de tomar decisiones estratégicas los deseos y necesidades de sus clientes reales y potenciales. Esto significa que primero busca el mercado/nicho/necesidades y luego crea el producto en base a ello y no al revés. Por ello, lo primero no es la idea, ¡lo primero es encontrar la necesidad!

[IA Aprendizaje] Probabilidad: El método clásico II Clasificación bayesiana ingenua

En primer lugar pido disculpas por haber dejado tanto espacio entre una entrada y otra, pero es que estoy en un momento de mi vida bastante complicado, no he parado de estudiar (en el ámbito universitario, que todos los que andamos por aquí siempre estamos estudiando nuevos frameworks constantemente) y por otra parte ha sido un verano muy ajetreado porque además, he vuelto a emprender 😀
Hoy vamos a hablar de la clasificación bayesiana ingenua, que no es más que un clasificador probabilístico basado en la aplicación del teorema de Bayes. La coletilla de ingenuo viene porque al crear el clasificador asumimos que las variables que utilizaremos son totalmente independientes unas de otras (esto es, recordemos,  ausencia de correlación o P(A,B)=P(A)*P(B)). Como anécdota mencionar que una de las razones por las que se abandonó el método clásico de probabilidad en IA fue, en parte, por la introducción de esta hipótesis. Quizá más adelante (en función de mi tiempo y vuestra aceptación, feedback por favor), veremos que con las redes bayesianas no tenemos este inconveniente.

Teorema de Bayes



Recordemos que el teorema de bayes nos dice que P(A|B) = P(A|B)*P(A) / P(B) donde A se puede interpretar como «causa» y B como «efecto», es decir, A es una reacción a la acción de B o lo que es lo mismo, A tendría dependencia causal de B.
Por ejemplo, en el caso de los diagnósticos clínicos del campo de Medicina Basada en le Evidencia, tenemos que: P(síntoma|enfermedad) = 0.6 P(síntoma) = 0.15 y P(enfermedad) =0.05
A partir de estos datos podemos preguntarnos por la probabilidad de que la causa del síntoma de un paciente sea la enfermedad, y vendría dada por (teorema de Bayes):

P(enfermedad|síntoma)=P(sintoma|enfermedad)*P(enfermedad)/P(síntoma)  = 0.7*0.05/0.15 = 0.233.
Como se puede ver en el ejemplo, dada una serie de efectos se puede clasificar y, por definición, encontrar la causa que los provoca. Un ejemplo típico (aunque hay muchísimas aplicaciones,
sobre todo en el campo de la minería de datos) es el filtrado de Spam o correo basura. Así que nos ponemos manos a la obra para construir nuestro propio y simple, filtro de Spam.

Filtro de Spam


Para comenzar, utilizaremos la fórmula de Bayes de la siguiente manera: P(palabra|categoria)=P(categoria|palabra) * P(palabra) / P(categoria)
Donde la categoría puede tomar los siguientes valores: SPAM y NO_SPAM. Cuando tenemos calculada la probabilidad de cada palabra condicionada, faltaría multiplicar sus probabilidades para cada categoría: P(categoria|palabra1,palabra2,…,palabran) = P(categoria) SIGMA_POR P(palabra|categoria) Es decir, multiplicamos cada probabilidad P(palabrai|categoria) del texto que queremos
clasificar y además, también P(categoria).
Antes de poder clasificar un texto, tendremos que poder analizar una serie de datos previos, algo así como un entrenamiento. Necesitaremos ejemplos de correos clasificados como SPAM o NO_SPAM, cuantos más ejemplos proveamos, más probabilidad de acertar tendrá nuestro clasificador.
En el próximo ejemplo utilizaremos la función training() para realizar el entrenamiento. La función espera una lista con un campo para el texto y otro que indica si es SPAM o NO_SPAM:
[«Esto es un ejemplo de frase»,NO_SPAM]
Se utilizará la función auxiliar wordList() que devuelve una lista con todas las palabras del texto mayores de dos caracteres, en minúscula y sin repeticiones.
La función training genera cuatro variables que almacenan los parámetros para su posterior clasificación.
La variable cWords es una lista con las palabras del texto y el número de veces que aparece en cada categoría. La variable cCategories almacena las categorías con el número de textos que pertenecen a ella. {«SPAM»:2,»NO_SPAM»:3} y por último, las variables cTexts y cTotalWords guardan el número de textos totales que hay en el ejemplo y el número de palabras totales.
La función Classify() es la que hace los cálculos y determina a qué categoría pertenece el texto que le pasamos como parámetro. También espera que le pasemos las cuatro variables anteriores.

 

   1:  namespace Clasificador_de_Spam
   2:  {
   3:      class Program
   4:      {
   5:   
   6:          class Message
   7:          {
   8:              public string Text { get; set; }
   9:              public string CategoryName { get; set; }
  10:          }
  11:          class Category
  12:          {
  13:              public string Name { get; set; }
  14:              public int Total { get; set; }
  15:   
  16:          }
  17:          class WordPerCategory
  18:          {
  19:             
  20:              public string Word { get; set; }
  21:              public List<Category>CategoryPerWord { get; set; }
  22:          
  23:              
  24:              public Category this[string value]
  25:              {
  26:                  get
  27:                  {
  28:                      return this.CategoryPerWord.FirstOrDefault(c => c.Name.Equals(value));
  29:                  }
  30:              }
  31:          }
  32:   
  33:   
  34:          class Classify
  35:          {
  36:              public Classify()
  37:              {
  38:                  cWords = new List<WordPerCategory>();
  39:                  cCategories = new List<Category>();
  40:                  cTotalTexts = 0;
  41:                  cTotalWords = 0;
  42:              }
  43:              public List<WordPerCategory> cWords { get; set; }
  44:              public List<Category> cCategories { get; set; }
  45:              public int cTotalTexts { get; set; }
  46:              public int cTotalWords { get; set; }
  47:   
  48:   
  49:   
  50:              public List<string> WordList(string text)
  51:              {
  52:                  var wordsTemp = text.ToLower().Split(' ');
  53:                  var words = new List<string>();
  54:                  foreach (var word in wordsTemp)
  55:                      if (word.Count() > 2 && !words.Contains(word))
  56:                          words.Add(word);
  57:   
  58:                  return words;
  59:              }
  60:   
  61:              public void Training(List<Message> texts)
  62:              {
  63:   
  64:   
  65:                  foreach (var message in texts)
  66:                  {
  67:                      cTotalTexts++;
  68:                      if (cCategories.Any(c => c.Name.Equals(message.CategoryName)))
  69:                          cCategories.First(c => c.Name.Equals(message.CategoryName)).Total++;
  70:                      else
  71:                          cCategories.Add(new Category() { Name = message.CategoryName, Total = 1 });
  72:   
  73:                  }
  74:   
  75:                  foreach (var message in texts)
  76:                  {
  77:                      var words = WordList(message.Text);
  78:                      foreach (var word in words)
  79:                      {
  80:                         
  81:                          if (!cWords.Any(w => w.Word.Equals(word)))
  82:                          {
  83:                              cTotalWords++;
  84:                              var wordPerCategory = new WordPerCategory() { Word = word, CategoryPerWord = new List<Category>()};
  85:                              foreach (var cat in cCategories)                     
  86:                                  wordPerCategory.CategoryPerWord.Add(new Category(){Name = cat.Name, Total = 0});
  87:                          
  88:                              cWords.Add(wordPerCategory);
  89:                          }
  90:                          cWords.First(w => w.Word.Equals(word))[message.CategoryName].Total++;
  91:   
  92:                      }
  93:   
  94:                  }
  95:   
  96:   
  97:              }
  98:   
  99:   
 100:              public Category Classifier(string texto)
 101:              {
 102:                  Category category = new Category();
 103:                  double probCategory = 0.0;
 104:   
 105:                  foreach (var c in cCategories)
 106:                  {
 107:                      //Prob of the category
 108:                      double probC = (double) c.Total / cTotalTexts;
 109:                      var words = WordList(texto);
 110:                      double probTotal = probC;
 111:                      foreach (var w in words)
 112:                      {
 113:                          //Prob of the  word
 114:                          if (cWords.Any(word => word.Word.Equals(w)))
 115:                          {
 116:                              var word = cWords.First(wrd => wrd.Word.Equals(w));
 117:                              double probWord = (double)word[c.Name].Total / cTotalWords;
 118:                              //P(cat|word)
 119:                              double probCond = (double)probWord / probC;
 120:                              //P(word|cat)
 121:                              double prob = (double)(probCond * probWord) / probC;
 122:                              probTotal *= (double)prob;
 123:                          }
 124:                      }
 125:                      if (probCategory < probTotal)
 126:                      {
 127:                          category = c;
 128:                          probCategory = probTotal;
 129:                      }
 130:                  }
 131:   
 132:                  return category;
 133:              }
 134:          }
 135:   
 136:          static void Main(string[] args)
 137:          {
 138:             List<Message> text = new List<Message>()
 139:                                       {
 140:                                           new Message(){CategoryName = "SPAM",Text = "Juega al poker online"},
 141:                                           new Message(){CategoryName = "NO_SPAM",Text = "Mañana vamos al cine"},
 142:                                           new Message(){CategoryName = "SPAM",Text = "Juega en los mejores casinos de poker"},
 143:                                           new Message(){CategoryName = "SPAM",Text = "Felicidades usted ha sido seleccionado para ganar un Microsoft Surface"},
 144:                                           new Message(){CategoryName = "NO_SPAM",Text = "La IA es una gran disciplina, es el punto de convergencia perfecto entre las mates y la program"},
 145:                                       };
 146:              Classify antiSpam = new Classify();
 147:              antiSpam.Training(text);
 148:              Category category = antiSpam.Classifier("Juega al poker y gana");
 149:              Console.WriteLine("Se trata de {0}.",category.Name);
 150:   
 151:              Console.Read();
 152:          }
 153:      }
 154:  }

 

 

Pues ya tenemos nuestro filtro anti spam definido. Hay que tener en cuenta que el entrenamiento ha sido con muy pocos datos, aunque aun así es relativamente fiable. Por otra parte, lo hemos definido para N categorías, cambiando dichas categorías podríamos clasificar cualquier otra cosa (por ejemplo qué palabras pertenecen a qué lenguaje).

 
Bibliografía:

Francisco Javier Díez, Probabilidad y teoría de la decisión en medicina

Alberto Garcia Serrano, Inteligencia Artificial Fundamentos, práctica y aplicaciones.

Stuart Russell, Peter Norvig Inteligencia Artificial: Un enfoque moderno.

Referencias

[IA Aprendizaje] Probabilidad: El método clásico I