Articoli (Persone, Business, Tecnologia)

Just Used Machine Learning To Be a Better Learner

New knowledge acquisition and comprehension are an essential part of the process and, in the last months, I studied a lot about neuroscience to better understand how our brain handles and stores information.
In parallel, while I started to digitally store useful concepts, I stumbled into Tiago Forte’s “Second Brain”, a personal knowledge management system that can be used to build a secondary information repository to pair with the long-term memory of our main brain and to use when needed.
To better accomplish this, following the pure spirit of Second Brain, mixing knowledge from different areas, I decided to build a tool for myself, blending programming, machine learning and neuroscience knowledge, making my Second Brain more powerful and modular.
In this story, I will describe not only the technical aspects but also the reason behind the main features, as my primary goal was to have a tool to assist me in my knowledge gathering.
Let’s start from our (main) brain to give some context.

How our brain works

Our brain, an incredibly complex and fascinating organ, basically has two distinct tools to handle information:
  • Working memory/short term memory (STM): very limited and volatile, where new info are first acquired
  • Long term memory (LTM): virtually infinite, where everything we know is stored
How STM and LTM communicate
These two parts can exchange info in both ways, from STM to LTM through the “consolidation “ and “reconsolidation” process and from LTM to STM through the “reactivation” process.
The core of these processes is the “chunk”, a compact package of known and well-understood information, a basic concept.
So, approaching a new subject, we can build chunks “decomposing” a more complex topic into smaller ones.
But there are other cool aspects about chunks:
  • they can be assembled and “zipped” in the working memory to make it more efficient
  • they can be transferred and applied to other knowledge domains
  • they greatly improve sharing of information between memories.
Let me show a simple example: imagine you have to memorize these two alphanumeric sequences:
  • 34ryf%s7wt
  • 4x+5y^2=12
Their length is the same but I’m sure the second sequence is much easier to handle, because the composition of chunks about “equation”, “addition”, “variable”, “power” gives a “meaning” to it, helping to deal with it in the STM.

The (Digital) Second Brain

What pushed me to review my approach was realizing my knowledge retention was low.
In the last years, I read tons of nonfiction books and, generally, was exposed to a lot of information but this does not mean be available when needed.
And does not matter how many books you read or podcast you follow, it’s just a vanity metric if knowledge is not actionable.
So I started to summarize and saving a note if I encounter an interesting concept.
I’m currently using Google Keep but Notion or Obsidian are excellent alternatives.

Google Keep -The starting point. Notes!
I built a simple notes taxonomy using tags, introducing concepts such as knowledge area (Physics, Biology, Leadership and so on), source (the source name, for example, a book title or a podcast name), the resource type (book, podcast, course, and so on) and a basic workflow to maintain consistency.
But this is not enough, especially considering a large knowledge base (KB) with potentially thousands of notes.
I needed something to handle this KB in a more “brainy” way, using chunks as building blocks, to form new connections between concepts — even from different topics — and generally be more effective in the process of consolidation, passing from the main, biological STM to the digital LTM of Second Brain itself.
So let me introduce the Second Brain Interface!

Tech overview

Second Brain Interface (SBI from now) is a Python custom application integrated with different third parties services.
This is the logical architecture:

Second Brain Interface — Logical architecture
Google Keep is the entry point for data and SBI can read and write from it, via its APIs.
The main chunk metadata (title, text, labels, various timestamps) are taken from there, but I also added a review date (more on this later) and, to persist it, I used Firestore as a persistence layer, so SBI can perform CRUD operations using an adapter.
But let’s see the main features more in detail.

Feature 1 — Vocal interface

I just couldn’t resist the temptation to have a vocal interface for talking to my Second Brain in a way similar to Alexa or Google Home and listen for some replies.
Moreover, putting some constraints (wake up word, working offline), I had the chance to work with something very interesting.
Indeed, after some searches, I found a very interesting platform called Picovoice and built around it the Intent Manager, to have a wake-up word activation and the possibility to work with intents, mapping specific actions to specific vocal inputs.
I’m able to map specific intent and parameters using directly the Picovoice Console, a web application to train and download a model to use locally.
Once the intent is understood, its payload can be used in some specific business logic, for example querying Google Keep.
I also added Google speech technology for an “online mode” for some voice commands, because the drawback of Picovoice intent engine is you have to train the model in advance to be able to catch a specific phrase and, for some features, this is not feasible.

Feature 2 — Stats about the knowledge base

I think is important to have some awareness about how KB is evolving and its composition, so basically I did some “EDA” (Exploratory Data Analysis) on it.
Besides asking vocally some info, I built a web dashboard to add a visual interface, using the Dash framework to display graphs.
I’m not a frontend developer — it will be very clear seeing the screenshots :) — but, with Dash, I was able to build a React application wrapped in Python code.
Thanks to Keep integration is straightforward to put all the data in a Pandas DataFrame and start digging, using Plotly libraries.
Besides some basic info, I built:
  • Wordcloud, using a specific library, to see an insight regarding all the available chunks. The use of NLTK Lemmatizer helped to normalize words because, by definition, lemmatization in linguistics is the process of grouping together the inflected forms of a word so they can be analyzed as a single item, identified by the word’s lemma.
  • Histograms to see the different chunks labels type (knowledge areas, sources, resources types)
I think I will add some cooler reports such as animated infographics to have a better grasp of KB changes through time, seeing how new areas are added or the evolution of existing ones.

Feature 3 — New knowledge acquisition and automatic summarization

This is another feature where ML — NLP more precisely — comes into play
The possibility to automatically summarize some text is very interesting, especially if paired with Wikipedia.
Of course, this process should not be totally delegated to ML, because is a very important action to do with our brain to enhance comprehension and knowledge extraction, but can help as a preliminary step in a more computer-assisted workflow.
Technically, I’m using a summarization service available on Hugging Face, where is possible to pick, among several, a trained model for a specific task (text summarization in this case) and send to it a payload to process.

Hugging Face interface
Here’s the simple code, basically a POST request with the text to summarize in the payload.
To make it work, you need an account to Hugging Face (to obtain the API token) and install their libraries.
class Summarizer:

def __init__(self):
        model_name = sb_properties.SUMMARIZER_MODEL
        api_token = sb_helper.get_hf_api()
        logger.debug("Using {} as summarization model".format(model_name))
        self.api_url = "" + model_name
        self.model_id = model_name
        self.headers = {"Authorization": "Bearer {}".format(api_token)}
def summarize_text(self, text_to_summarize):
        payload = {"inputs" : text_to_summarize}
        response =, headers=self.headers, json=payload)
        return response.json()
The text is extracted from the summary part of a Wikipedia page (if any)
import wikipedia

def retrieve_summary(self,topic):
            page =
            logger.debug("Found page with title {}".format(page.title))
            logger.debug("Found page with url {}".format(page.url))
            return page.summary
        except wikipedia.exceptions.PageError:
            logger.error("Page about topic {} not found ".format(topic))
        except wikipedia.exceptions.DisambiguationError:    
            logger.error("Disambiguation error for topic {} ".format(topic))
        except Exception as e:
            logger.error("Error %s", e)
Let’s see the difference between the summarized text in the example…
The Alcubierre drive, Alcubierre warp drive, or Alcubierre metric (referring to metric tensor) is a speculative warp drive idea based on a solution of Einstein's field equations in general relativity as proposed by theoretical physicist Miguel Alcubierre during his PhD study at the University of Wales, Cardiff, by which a spacecraft could achieve apparent faster-than-light travel if a configurable energy-density field lower than that of vacuum (that is, negative mass) could be created.

Rather than exceeding the speed of light within a local reference frame, a spacecraft would traverse distances by contracting space in front of it and expanding space behind it, resulting in effective faster-than-light travel.
and the original taken from Wikipedia
The Alcubierre drive, Alcubierre warp drive, or Alcubierre metric (referring to metric tensor) is a speculative warp drive idea based on a solution of Einstein's field equations in general relativity as proposed by theoretical physicist Miguel Alcubierre during his PhD study at the University of Wales, Cardiff, by which a spacecraft could achieve apparent faster-than-light travel if a configurable energy-density field lower than that of vacuum (that is, negative mass) could be created.[1][2]

Rather than exceeding the speed of light within a local reference frame, a spacecraft would traverse distances by contracting space in front of it and expanding space behind it, resulting in effective faster-than-light travel. Objects cannot accelerate to the speed of light within normal spacetime; instead, the Alcubierre drive shifts space around an object so that the object would arrive at its destination more quickly than light would in normal space without breaking any physical laws.[3]
Although the metric proposed by Alcubierre is consistent with the Einstein field equations, construction of such a drive is not necessarily possible. The proposed mechanism of the Alcubierre drive implies a negative energy density and therefore requires exotic matter or manipulation of dark energy.[4] If exotic matter with the correct properties cannot exist, then the drive cannot be constructed. At the close of his original article,[5] however, Alcubierre argued (following an argument developed by physicists analyzing traversable wormholes[6][7]) that the Casimir vacuum between parallel plates could fulfill the negative-energy requirement for the Alcubierre drive.

Another possible issue is that, although the Alcubierre metric is consistent with Einstein's equations, general relativity does not incorporate quantum mechanics. Some physicists have presented arguments to suggest that a theory of quantum gravity (which would incorporate both theories) would eliminate those solutions in general relativity that allow for backwards time travel (see the chronology protection conjecture) and thus make the Alcubierre drive invalid.
Not bad as a result, even if is still too complex to be considered a single chunk, and the omitted part about Alcubierre drive feasibility is very interesting.
Actually, I’m using the “google/pegasus-large” model but could be nice to use different models at the same time and pick the best-extracted summarization.

Feature 4 — Checking the chunks

As I introduced the chunk concept much later, there are still a lot of notes, especially older ones, that cannot be considered chunks (meaning clear, compact and with a specific main concept).
Moreover, is not always easy to summarize something on the fly, so a consistent “housekeeping” activity is necessary to maintain chunks' quality.
But it’s ok, is part of the learning process itself to improve what is known, “refactoring” or expanding.
There is not a straight possibility to define when a note is also a chunk, so I’m just using the number of lemmas to check if is within a determined threshold (40 lemmas actually). If not, there are chances that the note has to be rewritten and/or split.
Second Brain Interface — Chunks to check
Not a perfect approach, but we still have our main brain to decide what to do :)

Feature 5 — Reviewing chunks and automatic questions generation

Having several hundreds of available chunks and with a perspective of thousands in the future, is important to have a review mechanism, to periodically assess and improve the chunks, because having a Second Brain means actionable knowledge.
So, to keep track of when a chunk was reviewed, I added to it the review date. When the chunk is created, it’s equal to its creation date but, as time goes by, all the chunks with review date in a certain time window are found as “review needed”.
The idea is to read and at least acknowledge their presence again, mimic the main brain reactivation process (from LTM to STM), enabling a spaced repetition approach.
Second Brain Interface — Chunks to review
But is there a way to facilitate this review process?
It would be nice if, during the review, instead of just reading the text, I had to answer some generated questions about it?
NLP can help once again, using a package called Questgen AI.
Under the hood, it uses a relatively new approach to NLP models, called T5 (Text To Text Transfer Transformer) where both input and output are text, allowing transformation, in this case, to obtain questions from statements.

Second Brain Interface — Questions generation for a chunk
In the above example, the first question is perfect, the second not so much, probably needs to be rewritten in a closer way to the data on which the model was trained but is a good start and push me to write a chunk in a better way.
The code is very simple:
from Questgen import main

def generate_questions(self, question_context):
        payload = {
            "input_text": question_context            
        output = main.QGen().predict_shortq(payload)
        if output:
            return output['questions']
            return None

Feature 6 — Search and knowledge “navigation”

This is the most interesting feature, as I tried to build a visual representation of KB to acquire new insights and to discover unexpected relationships between chunks, mimic how our brain works in diffuse mode when different and wider areas are connected to create new knowledge.
Thre is a search function, that connects all the chunks containing the input text
But there is more!
I used a similarity function to find the chunks "similar” to the selected one below a certain threshold (higher value means more similar)
So, just browsing the nodes and changing the threshold value, is possible to achieve a sort of serendipity through similarity and explore the KB differently.

Technically, this is possible by computing the
cosine similarity matrix of the vector obtained by vectorizing the chunks text after some cleaning.
import string
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from nltk.corpus import stopwords
import pandas as pdimport re
from nltk.corpus import wordnet
import nltk
from nltk.stem import WordNetLemmatizer

def clean_string(text):
    text = ''.join([word for word in text if word not in string.punctuation])
    text = text.lower()
    text = re.sub(r'[^\x00-\x7F]+', ' ', text)
    text = re.sub(r'\s{2,}', ' ', text)
    text = ' '.join([word for word in text.split() if word not in stop_words])
    text = ' '.join([lemmatizer.lemmatize(w) for w in nltk.word_tokenize(text)])   
    return text
def compute_chunks_similarity(self, column):
            cleaned_strings = list(map(clean_string, self.model[column].values))
            vectorizer = CountVectorizer().fit_transform(cleaned_strings)
            vectors = vectorizer.toarray()
return (cosine_similarity(vectors))


I enjoyed spending time designing and developing SBI, as I gained insights into new subjects, practiced acquired skills, and, generally, because learning by doing is awesome.
Besides, Machine Learning once again showed its utility in very specific domains, such as personal knowledge management, as I previously explored in fitness.
I’ll continue to add features and improve them, considering SBI as an assistant in my personal growth.
The next major two will be:
  • using LDA (Latent Dirichlet allocation) to find and show hidden topics and connections in the KB,
  • have the possibility to manually link chunks to form a cluster of specific knowledge.
But, besides technicalities and tools, my main goal is to show how knowledge acquisition can be approached systematically and coherently because curiosity and willingness to learn are, of course, the foundations but they need to be focused and consistent.
So, next time you're reading a book, seeing a video, or listen to a podcast and something is interesting going on, pause for a moment and pick a note about it.
It will be weird at first, you will feel slowed down but the fact is, every time, you’ll be making a long-term investment on the most valuable asset you have: your knowledge.
Sono un Coach specializzato e IT Mentor, con 25 anni di esperienza nel settore IT. Se vuoi migliorare la parte Tech della tua Azienda o migliorare te stesso/a, sono qui per supportarti. Scopriamo insieme come
Made on