Christopher Capobianco
commited on
Commit
•
d8d58f7
1
Parent(s):
7234ab5
Add project pages and move intro page
Browse files- Home.py +72 -0
- app.py +26 -69
- projects/02_Movie_Recommendation.py +59 -0
- projects/04_Weather_Classification.py +60 -0
- projects/05_Stock_Market.py +159 -0
- projects/06_Generative_Music.py +177 -0
- projects/07_LLM_Fine_Tuned.py +64 -0
Home.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from PIL import Image
|
3 |
+
|
4 |
+
# Page title
|
5 |
+
st.title("Chris Capobianco's ML Portfolio")
|
6 |
+
|
7 |
+
st.markdown('Hello, welcome to my ML portfolio.')
|
8 |
+
st.markdown('Please have a look at the descriptions below, and select a project from the sidebar.')
|
9 |
+
|
10 |
+
st.header('Projects', divider='red')
|
11 |
+
|
12 |
+
mv = Image.open("assets/movie.jpg")
|
13 |
+
# wp = Image.open("assets/weather.png")
|
14 |
+
sm = Image.open("assets/stock-market.png")
|
15 |
+
mu = Image.open("assets/music.jpg")
|
16 |
+
llm = Image.open("assets/llm.png")
|
17 |
+
|
18 |
+
with st.container():
|
19 |
+
text_column, image_column = st.columns((3,1))
|
20 |
+
with text_column:
|
21 |
+
st.subheader("Movie Recommendation", divider="green")
|
22 |
+
st.markdown("""
|
23 |
+
- Created a content based recommendation system using cosine similarity
|
24 |
+
- Trained on almost 5k movies and credits from the TMDB dataset available at Kaggle
|
25 |
+
""")
|
26 |
+
with image_column:
|
27 |
+
st.image(mv)
|
28 |
+
|
29 |
+
# with st.container():
|
30 |
+
# text_column, image_column = st.columns((3,1))
|
31 |
+
# with text_column:
|
32 |
+
# st.subheader("Weather Classification", divider="green")
|
33 |
+
# st.markdown("""
|
34 |
+
# - Created a Random Forest classification model to predict the weather
|
35 |
+
# - Trained on three years of data for the city of Seattle, Washington
|
36 |
+
# """)
|
37 |
+
# with image_column:
|
38 |
+
# st.image(wp)
|
39 |
+
|
40 |
+
with st.container():
|
41 |
+
text_column, image_column = st.columns((3,1))
|
42 |
+
with text_column:
|
43 |
+
st.subheader("Stock Market Forecast", divider="green")
|
44 |
+
st.markdown("""
|
45 |
+
- Created a two layer GRU model to forecast of stock prices
|
46 |
+
- Trained on 2006-2018 closing prices of four well known stocks
|
47 |
+
""")
|
48 |
+
with image_column:
|
49 |
+
st.image(sm)
|
50 |
+
|
51 |
+
with st.container():
|
52 |
+
text_column, image_column = st.columns((3,1))
|
53 |
+
with text_column:
|
54 |
+
st.subheader("Generative Music", divider="green")
|
55 |
+
st.markdown("""
|
56 |
+
- Created a LSTM model to generate music
|
57 |
+
- Trained on MIDI files from Final Fantasy series
|
58 |
+
""")
|
59 |
+
with image_column:
|
60 |
+
st.image(mu)
|
61 |
+
|
62 |
+
with st.container():
|
63 |
+
text_column, image_column = st.columns((3,1))
|
64 |
+
with text_column:
|
65 |
+
st.subheader("Fine Tuned LLM", divider="green")
|
66 |
+
st.warning("**Work In Progress**")
|
67 |
+
st.markdown("""
|
68 |
+
- Fine tuned a LLM to act like math assistant
|
69 |
+
- The base model is Meta's Llama 3.1 (8B) Instruct
|
70 |
+
""")
|
71 |
+
with image_column:
|
72 |
+
st.image(llm)
|
app.py
CHANGED
@@ -1,72 +1,29 @@
|
|
1 |
import streamlit as st
|
2 |
-
from PIL import Image
|
3 |
|
4 |
# Page title
|
5 |
-
st.
|
6 |
-
|
7 |
-
st.
|
8 |
-
|
9 |
-
|
10 |
-
st.
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
# with text_column:
|
32 |
-
# st.subheader("Weather Classification", divider="green")
|
33 |
-
# st.markdown("""
|
34 |
-
# - Created a Random Forest classification model to predict the weather
|
35 |
-
# - Trained on three years of data for the city of Seattle, Washington
|
36 |
-
# """)
|
37 |
-
# with image_column:
|
38 |
-
# st.image(wp)
|
39 |
-
|
40 |
-
with st.container():
|
41 |
-
text_column, image_column = st.columns((3,1))
|
42 |
-
with text_column:
|
43 |
-
st.subheader("Stock Market Forecast", divider="green")
|
44 |
-
st.markdown("""
|
45 |
-
- Created a two layer GRU model to forecast of stock prices
|
46 |
-
- Trained on 2006-2018 closing prices of four well known stocks
|
47 |
-
""")
|
48 |
-
with image_column:
|
49 |
-
st.image(sm)
|
50 |
-
|
51 |
-
with st.container():
|
52 |
-
text_column, image_column = st.columns((3,1))
|
53 |
-
with text_column:
|
54 |
-
st.subheader("Generative Music", divider="green")
|
55 |
-
st.markdown("""
|
56 |
-
- Created a LSTM model to generate music
|
57 |
-
- Trained on MIDI files from Final Fantasy series
|
58 |
-
""")
|
59 |
-
with image_column:
|
60 |
-
st.image(mu)
|
61 |
-
|
62 |
-
with st.container():
|
63 |
-
text_column, image_column = st.columns((3,1))
|
64 |
-
with text_column:
|
65 |
-
st.subheader("Fine Tuned LLM", divider="green")
|
66 |
-
st.warning("**Work In Progress**")
|
67 |
-
st.markdown("""
|
68 |
-
- Fine tuned a LLM to act like math assistant
|
69 |
-
- The base model is Meta's Llama 3.1 (8B) Instruct
|
70 |
-
""")
|
71 |
-
with image_column:
|
72 |
-
st.image(llm)
|
|
|
1 |
import streamlit as st
|
|
|
2 |
|
3 |
# Page title
|
4 |
+
st.set_page_config(page_title="Chris Capobianco's Profile", page_icon=':rocket:', layout='wide')
|
5 |
+
|
6 |
+
home = st.Page('Intro.py', title = 'Home')
|
7 |
+
|
8 |
+
movie_recommendation = st.Page('projects/02_Movie_Recommendation.py', title='Movie Recommendation')
|
9 |
+
# weather_classification = st.Page('projects/04_Weather_Classification.py', title='Weather Classification')
|
10 |
+
stock_market = st.Page('projects/05_Stock_Market.py', title='Stock Market Forecast')
|
11 |
+
generative_music = st.Page('projects/06_Generative_Music.py', title='Generative Music')
|
12 |
+
llm_fine_tune = st.Page('projects/07_LLM_Fine_Tuned.py', title='Fine Tuned LLM')
|
13 |
+
|
14 |
+
pg = st.navigation(
|
15 |
+
{
|
16 |
+
'Home': [
|
17 |
+
home
|
18 |
+
],
|
19 |
+
'Projects': [
|
20 |
+
movie_recommendation,
|
21 |
+
# weather_classification,
|
22 |
+
stock_market,
|
23 |
+
generative_music,
|
24 |
+
llm_fine_tune
|
25 |
+
]
|
26 |
+
}
|
27 |
+
)
|
28 |
+
|
29 |
+
pg.run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
projects/02_Movie_Recommendation.py
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pickle
|
3 |
+
|
4 |
+
st.header('Movie Recommendation', divider='green')
|
5 |
+
|
6 |
+
@st.cache_resource
|
7 |
+
def load_model():
|
8 |
+
model_file = open('./models/movie_recommendation_model.pkl', 'rb')
|
9 |
+
movies_df = pickle.load(model_file)
|
10 |
+
cosine_sim = pickle.load(model_file)
|
11 |
+
indices = pickle.load(model_file)
|
12 |
+
model_file.close()
|
13 |
+
return movies_df, cosine_sim, indices
|
14 |
+
|
15 |
+
# Load the Model
|
16 |
+
movies_df, cosine_sim, indices = load_model()
|
17 |
+
|
18 |
+
@st.cache_data
|
19 |
+
def get_recommendations(title, cosine_sim = cosine_sim):
|
20 |
+
if title in indices:
|
21 |
+
idx = indices[title]
|
22 |
+
else:
|
23 |
+
print(f"Unable to find a matching movie title: {title}")
|
24 |
+
return []
|
25 |
+
similarity_scores = list(enumerate(cosine_sim[idx]))
|
26 |
+
similarity_scores = sorted(similarity_scores, key = lambda x: x[1], reverse = True)
|
27 |
+
similarity_scores = similarity_scores[1:11]
|
28 |
+
# (a, b) where a is id of movie, b is similarity_scores
|
29 |
+
movies_indices = [ind[0] for ind in similarity_scores]
|
30 |
+
movies = movies_df["title"].iloc[movies_indices]
|
31 |
+
return movies
|
32 |
+
|
33 |
+
# Get movie recommendations
|
34 |
+
st.markdown("#### What is a Recommendation System?")
|
35 |
+
st.markdown("Recommendation systems suggest recommendations to users depending on a variety of criteria.")
|
36 |
+
st.markdown('''There are 3 types of recommendation systems:
|
37 |
+
1. Demographic Filtering: The recommendations are the same for every user. They are generalized, not personalized. These types of systems are behind sections like *Top Trending*.
|
38 |
+
2. Content-based Filtering: These suggest recommendations based on the item metadata (movie, product, song, etc). Here, the main idea is if a user likes an item, then the user will also like items similar to it.
|
39 |
+
3. Collaboration-based Filtering: These systems make recommendations by grouping the users with similar interests. For this system, metadata of the item is not required.
|
40 |
+
''')
|
41 |
+
st.markdown("In this project, we are building a **Content-based** recommendation engine for movies titles.")
|
42 |
+
tmdb_data_url = "https://www.kaggle.com/datasets/tmdb/tmdb-movie-metadata"
|
43 |
+
st.markdown("To build this recommendation system, we will be using almost 5k movies and credits from the TMDB dataset available at [Kaggle](%s)" % tmdb_data_url)
|
44 |
+
|
45 |
+
st.divider()
|
46 |
+
|
47 |
+
st.markdown("Enter the title of a movie to see a list of 10 recommendations (e.g. The Dark Knight Rises, The Avengers, etc)")
|
48 |
+
title = st.text_input("Movie Title")
|
49 |
+
if title != "":
|
50 |
+
movies = get_recommendations(title)
|
51 |
+
if len(movies) > 0:
|
52 |
+
output = ''
|
53 |
+
count = 1
|
54 |
+
for m in movies:
|
55 |
+
output += f"{count}. {m}\n"
|
56 |
+
count += 1
|
57 |
+
st.markdown(output)
|
58 |
+
else:
|
59 |
+
st.warning(f"Unable to find a movie matching title: {title}")
|
projects/04_Weather_Classification.py
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import pickle
|
4 |
+
import sklearn
|
5 |
+
from PIL import Image
|
6 |
+
|
7 |
+
@st.cache_resource
|
8 |
+
def load_model():
|
9 |
+
model_file = open('./models/weather_prediction_model.pkl', 'rb')
|
10 |
+
rfc = pickle.load(model_file)
|
11 |
+
oe = pickle.load(model_file)
|
12 |
+
sc = pickle.load(model_file)
|
13 |
+
images = pickle.load(model_file)
|
14 |
+
model_file.close()
|
15 |
+
return rfc, oe, sc, images
|
16 |
+
|
17 |
+
@st.cache_data
|
18 |
+
def load_icons():
|
19 |
+
dz = Image.open("assets/drizzle.png")
|
20 |
+
rn = Image.open("assets/rain.png")
|
21 |
+
sn = Image.open("assets/sun.png")
|
22 |
+
sw = Image.open("assets/snow.png")
|
23 |
+
fg = Image.open("assets/fog.png")
|
24 |
+
return dz, rn, sn, sw, fg
|
25 |
+
|
26 |
+
@st.cache_data
|
27 |
+
def get_prediction(input):
|
28 |
+
if len(input) != 4:
|
29 |
+
return None
|
30 |
+
input = pd.DataFrame([input], columns = ['precipitation', 'temp_max', 'temp_min', 'wind'])
|
31 |
+
X_predict = sc.transform(input)
|
32 |
+
y_predict = [rfc.predict(X_predict)]
|
33 |
+
weather_predict = oe.inverse_transform(y_predict)[0]
|
34 |
+
return weather_predict[0]
|
35 |
+
|
36 |
+
# Load the Model
|
37 |
+
rfc, oe, sc, images = load_model()
|
38 |
+
|
39 |
+
# Load the Icons
|
40 |
+
dz, rn, sn, sw, fg = load_icons()
|
41 |
+
|
42 |
+
st.header('Weather Classification', divider='green')
|
43 |
+
|
44 |
+
st.markdown("Change the sliders below to see the classification")
|
45 |
+
|
46 |
+
# Get input parameters
|
47 |
+
precipitation = st.slider('Precipitation (mm)', 0.0, 100.0, 0.0, 1.0)
|
48 |
+
temp_min = st.slider('Minimum Temperature (C)', -10.0, 20.0, 8.0, 1.0)
|
49 |
+
temp_max = st.slider('Maximum Temperature (C)', -2.0, 40.0, 15.0, 1.0)
|
50 |
+
wind = st.slider('Wind Speed (m/s)', 0.0, 10.0, 3.0, 1.0)
|
51 |
+
|
52 |
+
# Get weather prediction
|
53 |
+
weather = get_prediction([precipitation, temp_min, temp_max, wind])
|
54 |
+
if weather == None:
|
55 |
+
st.warning("Unknown Weather Classification")
|
56 |
+
else:
|
57 |
+
icon = Image.open(images[weather])
|
58 |
+
|
59 |
+
st.info("Weather Classification:")
|
60 |
+
st.image(icon, caption = weather, width = 250)
|
projects/05_Stock_Market.py
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pickle
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
from PIL import Image
|
5 |
+
|
6 |
+
@st.cache_resource
|
7 |
+
def load_model():
|
8 |
+
model_file = open('./models/stock_market_model.pkl', 'rb')
|
9 |
+
amazon_predictions = pickle.load(model_file)
|
10 |
+
amazon_scores = pickle.load(model_file)
|
11 |
+
google_predictions = pickle.load(model_file)
|
12 |
+
google_scores = pickle.load(model_file)
|
13 |
+
ibm_predictions = pickle.load(model_file)
|
14 |
+
ibm_scores = pickle.load(model_file)
|
15 |
+
microsoft_predictions = pickle.load(model_file)
|
16 |
+
microsoft_scores = pickle.load(model_file)
|
17 |
+
model_file.close()
|
18 |
+
return amazon_predictions, amazon_scores, google_predictions, google_scores, ibm_predictions, ibm_scores, microsoft_predictions, microsoft_scores
|
19 |
+
|
20 |
+
# Load Image
|
21 |
+
gru = Image.open("assets/gru.png")
|
22 |
+
nn = Image.open("assets/nn.png")
|
23 |
+
|
24 |
+
# Load the Model
|
25 |
+
amazon_predictions, amazon_scores, google_predictions, google_scores, ibm_predictions, ibm_scores, microsoft_predictions, microsoft_scores = load_model()
|
26 |
+
|
27 |
+
st.header('Stock Market Forecast', divider='green')
|
28 |
+
|
29 |
+
st.markdown("#### Time Series Forecasting")
|
30 |
+
st.markdown("Time series forecasting uses information regarding historical values and associated patterns to predict future activity. Most often, this relates to trend analysis, cyclical fluctuation analysis, and issues of seasonality. As with all forecasting methods, success is not guaranteed.")
|
31 |
+
|
32 |
+
st.markdown("#### GRU Model")
|
33 |
+
st.markdown("Gated recurrent unit is essentially a simplified LSTM. It has the exact same role in the network. The main difference is in the number of gates and weights — GRU is somewhat simpler. It has 2 gates. Since it does not have an output gate, there is no control over the memory content. The update gate controls the information flow from the previous activation, and the addition of new information as well, while the reset gate is inserted into the candidate activation.")
|
34 |
+
|
35 |
+
st.image(gru, caption = "Gated Recurrent Unit (GRU)")
|
36 |
+
|
37 |
+
st.markdown("Below are the analyses of four stocks using a GRU-based model: *Amazon*, *Google*, *IBM* and *Microsoft*")
|
38 |
+
st.markdown("The model consists of the following: A single input and output dimension (i.e. Closing Price) and two hidden layers, with 32 GRU nodes per layer. These models were trained for 105 epochs each.")
|
39 |
+
st.image(nn, caption = "GRU-based Model")
|
40 |
+
|
41 |
+
st.divider()
|
42 |
+
|
43 |
+
st.markdown("Below each graph is the mean square error (MSE) for the train and test sets, where the test set consists of the last 20 days.")
|
44 |
+
|
45 |
+
fig1 = go.Figure()
|
46 |
+
fig1.add_trace(go.Scatter(go.Scatter(x=amazon_predictions['Date'], y=amazon_predictions['Train Prediction'],
|
47 |
+
mode='lines',
|
48 |
+
name='Train Prediction')))
|
49 |
+
fig1.add_trace(go.Scatter(x=amazon_predictions['Date'], y=amazon_predictions['Test Prediction'],
|
50 |
+
mode='lines',
|
51 |
+
name='Test Prediction'))
|
52 |
+
fig1.add_trace(go.Scatter(go.Scatter(x=amazon_predictions['Date'], y=amazon_predictions['Actual Value'],
|
53 |
+
mode='lines',
|
54 |
+
name='Actual Value')))
|
55 |
+
fig1.update_layout(
|
56 |
+
title="Amazon Stock Prediction",
|
57 |
+
xaxis_title="Date",
|
58 |
+
yaxis_title="Close (USD)",
|
59 |
+
showlegend=True,
|
60 |
+
template = 'plotly_dark'
|
61 |
+
)
|
62 |
+
|
63 |
+
annotations = []
|
64 |
+
annotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,
|
65 |
+
xanchor='left', yanchor='bottom',
|
66 |
+
text='',
|
67 |
+
showarrow=False))
|
68 |
+
fig1.update_layout(annotations=annotations)
|
69 |
+
st.plotly_chart(fig1, use_container_width=True)
|
70 |
+
|
71 |
+
# MSE on train and test sets
|
72 |
+
st.markdown(f"Train MSE: {amazon_scores[0]:.3f}, Test MSE: {amazon_scores[1]:.3f}")
|
73 |
+
|
74 |
+
fig2 = go.Figure()
|
75 |
+
fig2.add_trace(go.Scatter(go.Scatter(x=google_predictions['Date'], y=google_predictions['Train Prediction'],
|
76 |
+
mode='lines',
|
77 |
+
name='Train Prediction')))
|
78 |
+
fig2.add_trace(go.Scatter(x=google_predictions['Date'], y=google_predictions['Test Prediction'],
|
79 |
+
mode='lines',
|
80 |
+
name='Test Prediction'))
|
81 |
+
fig2.add_trace(go.Scatter(go.Scatter(x=google_predictions['Date'], y=google_predictions['Actual Value'],
|
82 |
+
mode='lines',
|
83 |
+
name='Actual Value')))
|
84 |
+
fig2.update_layout(
|
85 |
+
title="Google Stock Prediction",
|
86 |
+
xaxis_title="Date",
|
87 |
+
yaxis_title="Close (USD)",
|
88 |
+
showlegend=True,
|
89 |
+
template = 'plotly_dark'
|
90 |
+
)
|
91 |
+
|
92 |
+
annotations = []
|
93 |
+
annotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,
|
94 |
+
xanchor='left', yanchor='bottom',
|
95 |
+
text='',
|
96 |
+
showarrow=False))
|
97 |
+
fig2.update_layout(annotations=annotations)
|
98 |
+
st.plotly_chart(fig2, use_container_width=True)
|
99 |
+
|
100 |
+
# MSE on train and test sets
|
101 |
+
st.markdown(f"Train MSE: {google_scores[0]:.3f}, Test MSE: {google_scores[1]:.3f}")
|
102 |
+
|
103 |
+
fig3 = go.Figure()
|
104 |
+
fig3.add_trace(go.Scatter(go.Scatter(x=ibm_predictions['Date'], y=ibm_predictions['Train Prediction'],
|
105 |
+
mode='lines',
|
106 |
+
name='Train Prediction')))
|
107 |
+
fig3.add_trace(go.Scatter(x=ibm_predictions['Date'], y=ibm_predictions['Test Prediction'],
|
108 |
+
mode='lines',
|
109 |
+
name='Test Prediction'))
|
110 |
+
fig3.add_trace(go.Scatter(go.Scatter(x=ibm_predictions['Date'], y=ibm_predictions['Actual Value'],
|
111 |
+
mode='lines',
|
112 |
+
name='Actual Value')))
|
113 |
+
fig3.update_layout(
|
114 |
+
title="IBM Stock Prediction",
|
115 |
+
xaxis_title="Date",
|
116 |
+
yaxis_title="Close (USD)",
|
117 |
+
showlegend=True,
|
118 |
+
template = 'plotly_dark'
|
119 |
+
)
|
120 |
+
|
121 |
+
annotations = []
|
122 |
+
annotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,
|
123 |
+
xanchor='left', yanchor='bottom',
|
124 |
+
text='',
|
125 |
+
showarrow=False))
|
126 |
+
fig3.update_layout(annotations=annotations)
|
127 |
+
st.plotly_chart(fig3, use_container_width=True)
|
128 |
+
|
129 |
+
# MSE on train and test sets
|
130 |
+
st.markdown(f"Train MSE: {ibm_scores[0]:.3f}, Test MSE: {ibm_scores[1]:.3f}")
|
131 |
+
|
132 |
+
fig4 = go.Figure()
|
133 |
+
fig4.add_trace(go.Scatter(go.Scatter(x=microsoft_predictions['Date'], y=microsoft_predictions['Train Prediction'],
|
134 |
+
mode='lines',
|
135 |
+
name='Train Prediction')))
|
136 |
+
fig4.add_trace(go.Scatter(x=microsoft_predictions['Date'], y=microsoft_predictions['Test Prediction'],
|
137 |
+
mode='lines',
|
138 |
+
name='Test Prediction'))
|
139 |
+
fig4.add_trace(go.Scatter(go.Scatter(x=microsoft_predictions['Date'], y=microsoft_predictions['Actual Value'],
|
140 |
+
mode='lines',
|
141 |
+
name='Actual Value')))
|
142 |
+
fig4.update_layout(
|
143 |
+
title="Microsoft Stock Prediction",
|
144 |
+
xaxis_title="Date",
|
145 |
+
yaxis_title="Close (USD)",
|
146 |
+
showlegend=True,
|
147 |
+
template = 'plotly_dark'
|
148 |
+
)
|
149 |
+
|
150 |
+
annotations = []
|
151 |
+
annotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,
|
152 |
+
xanchor='left', yanchor='bottom',
|
153 |
+
text='',
|
154 |
+
showarrow=False))
|
155 |
+
fig4.update_layout(annotations=annotations)
|
156 |
+
st.plotly_chart(fig4, use_container_width=True)
|
157 |
+
|
158 |
+
# MSE on train and test sets
|
159 |
+
st.markdown(f"Train MSE: {microsoft_scores[0]:.3f}, Test MSE: {microsoft_scores[1]:.3f}")
|
projects/06_Generative_Music.py
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import io
|
3 |
+
import pickle
|
4 |
+
import pretty_midi
|
5 |
+
import numpy as np
|
6 |
+
from music21 import instrument, note, stream, chord
|
7 |
+
from keras.saving import load_model
|
8 |
+
from scipy.io import wavfile
|
9 |
+
|
10 |
+
@st.cache_resource
|
11 |
+
def load_notes():
|
12 |
+
notes_filepath = 'models/music_notes.pkl'
|
13 |
+
with open(notes_filepath, 'rb') as filepath:
|
14 |
+
notes = pickle.load(filepath)
|
15 |
+
pitchnames = pickle.load(filepath)
|
16 |
+
n_vocab = pickle.load(filepath)
|
17 |
+
return (notes, pitchnames, n_vocab)
|
18 |
+
|
19 |
+
@st.cache_resource
|
20 |
+
def model_load():
|
21 |
+
model_filepath = 'models/music_model.keras'
|
22 |
+
model = load_model(model_filepath)
|
23 |
+
return model
|
24 |
+
|
25 |
+
@st.cache_data
|
26 |
+
def prepare_sequences(notes, pitchnames, n_vocab, sequence_length=100):
|
27 |
+
note_to_int = dict((note, number) for number, note in enumerate(pitchnames))
|
28 |
+
|
29 |
+
network_input = []
|
30 |
+
for i in range(0, len(notes) - sequence_length, 1):
|
31 |
+
sequence_in = notes[i:i + sequence_length]
|
32 |
+
sequence_out = notes[i + sequence_length]
|
33 |
+
network_input.append([note_to_int[char] for char in sequence_in])
|
34 |
+
|
35 |
+
return network_input
|
36 |
+
|
37 |
+
def generate_notes(model, network_input, pitchnames, n_vocab, nlength, istart=-1):
|
38 |
+
# pick a random sequence from the input as a starting point for the prediction
|
39 |
+
if istart < 0 or istart > len(network_input) - 1:
|
40 |
+
start = np.random.randint(0, len(network_input) - 1)
|
41 |
+
print(f"Starting Position = {start}")
|
42 |
+
else:
|
43 |
+
start = istart
|
44 |
+
|
45 |
+
int_to_note = dict((number, note) for number, note in enumerate(pitchnames))
|
46 |
+
|
47 |
+
pattern = network_input[start]
|
48 |
+
prediction_output = []
|
49 |
+
|
50 |
+
# generate nlength notes
|
51 |
+
for note_index in range(nlength):
|
52 |
+
prediction_input = np.reshape(pattern, (1, len(pattern), 1))
|
53 |
+
prediction_input = prediction_input / float(n_vocab)
|
54 |
+
|
55 |
+
prediction = model.predict(prediction_input, verbose=0)
|
56 |
+
|
57 |
+
index = np.argmax(prediction)
|
58 |
+
result = int_to_note[index]
|
59 |
+
prediction_output.append(result)
|
60 |
+
|
61 |
+
pattern.append(index)
|
62 |
+
pattern = pattern[1:len(pattern)]
|
63 |
+
|
64 |
+
return prediction_output
|
65 |
+
|
66 |
+
def create_midi(prediction_output, output_filepath):
|
67 |
+
offset = 0
|
68 |
+
output_notes = []
|
69 |
+
|
70 |
+
# create note and chord objects based on the values generated by the model
|
71 |
+
for pattern in prediction_output:
|
72 |
+
# pattern is a chord
|
73 |
+
if ('.' in pattern) or pattern.isdigit():
|
74 |
+
notes_in_chord = pattern.split('.')
|
75 |
+
notes = []
|
76 |
+
for current_note in notes_in_chord:
|
77 |
+
new_note = note.Note(int(current_note))
|
78 |
+
new_note.storedInstrument = instrument.Piano()
|
79 |
+
notes.append(new_note)
|
80 |
+
new_chord = chord.Chord(notes)
|
81 |
+
new_chord.offset = offset
|
82 |
+
output_notes.append(new_chord)
|
83 |
+
# pattern is a note
|
84 |
+
else:
|
85 |
+
new_note = note.Note(pattern)
|
86 |
+
new_note.offset = offset
|
87 |
+
new_note.storedInstrument = instrument.Piano()
|
88 |
+
output_notes.append(new_note)
|
89 |
+
|
90 |
+
# increase offset each iteration so that notes do not stack
|
91 |
+
offset += 0.5
|
92 |
+
|
93 |
+
# Write notes to a MIDI file
|
94 |
+
midi_stream = stream.Stream(output_notes)
|
95 |
+
midi_stream.write('midi', fp='output.mid')
|
96 |
+
|
97 |
+
def generate(model, network_input, pitchnames, n_vocab, nlength=500, istart=-1):
|
98 |
+
output_filepath = 'output.mid'
|
99 |
+
if nlength < 1:
|
100 |
+
print(f"Song length must be at least one note, defaulting to 250 notes")
|
101 |
+
nlength = 500
|
102 |
+
if nlength > 500:
|
103 |
+
print(f"Cannot exceed 500 notes for song length")
|
104 |
+
nlength = 500
|
105 |
+
|
106 |
+
prediction_output = generate_notes(model, network_input, pitchnames, n_vocab, nlength, istart)
|
107 |
+
create_midi(prediction_output, output_filepath)
|
108 |
+
return output_filepath
|
109 |
+
|
110 |
+
st.header('Generative Music', divider='green')
|
111 |
+
|
112 |
+
# Load notes
|
113 |
+
notes, pitchnames, n_vocab = load_notes()
|
114 |
+
|
115 |
+
# Prepare note sequences
|
116 |
+
network_input = prepare_sequences(notes, pitchnames, n_vocab)
|
117 |
+
|
118 |
+
# Load model
|
119 |
+
model = model_load()
|
120 |
+
|
121 |
+
st.markdown("#### What are Recurrent Neural Networks?")
|
122 |
+
st.markdown("A recurrent neural network is a class of artificial neural networks that make use of sequential information. They are called recurrent because they perform the same function for every single element of a sequence, with the result being dependent on previous computations. Whereas outputs are independent of previous computations in traditional neural networks.")
|
123 |
+
st.markdown("In this project we will use a **Long Short-Term Memory** (LSTM) network. They are a type of Recurrent Neural Network that can efficiently learn via gradient descent. Using a gating mechanism, LSTMs are able to recognise and encode long-term patterns. LSTMs are extremely useful to solve problems where the network has to remember information for a long period of time as is the case in music and text generation.")
|
124 |
+
st.markdown("#### Data")
|
125 |
+
st.markdown("The data that our model will be trained on will consist of piano MIDI files of Final Fantasy soundtracks, but any set of MIDI files consisting of a single instrument would work.")
|
126 |
+
st.markdown("The sequence of notes and chords from the MIDI files are broken down into increments of 100, which are used to predict the next note or chord.")
|
127 |
+
st.markdown("#### Model")
|
128 |
+
st.markdown("For this project we will use a network consisting of three LSTM layers, three Dropout layers, two Dense layers and one activation layer.")
|
129 |
+
st.markdown("It may be possible to improve this model by playing around with the the structure of the network, or adding new categories (e.g. varying note duration, rest periods between notes, etc). However, to achieve satisfying results with more classes we would also have to increase the depth of the LSTM network.")
|
130 |
+
st.markdown("*This is based off the tutorial by Sigurður Skúli [How to Generate Music using a LSTM Neural Network in Keras](https://towardsdatascience.com/how-to-generate-music-using-a-lstm-neural-network-in-keras-68786834d4c5)*")
|
131 |
+
st.divider()
|
132 |
+
|
133 |
+
midi_file = None
|
134 |
+
generated_midi = None
|
135 |
+
sample_midi = None
|
136 |
+
|
137 |
+
st.markdown("You can select one of the samples below")
|
138 |
+
|
139 |
+
sample_midi = st.selectbox(
|
140 |
+
'Select a sample MIDI file to play',
|
141 |
+
('assets/sample_01.mid', 'assets/sample_02.mid', 'assets/sample_03.mid'),
|
142 |
+
index=None,
|
143 |
+
placeholder="Please select a sample...",
|
144 |
+
)
|
145 |
+
|
146 |
+
st.markdown("Or generate a new sample by clicking the generate button")
|
147 |
+
|
148 |
+
n_notes = st.slider("How many notes do you want?", 1, 500, 250)
|
149 |
+
start_pos = st.slider("Where do you want to start? Negative will start at a random position.", -1, len(network_input) - 1, -1)
|
150 |
+
|
151 |
+
if st.button('Generate'):
|
152 |
+
with st.spinner(f"Generating a new MIDI file"):
|
153 |
+
generated_midi = generate(model, network_input, pitchnames, n_vocab, n_notes, start_pos)
|
154 |
+
|
155 |
+
st.divider()
|
156 |
+
|
157 |
+
if generated_midi:
|
158 |
+
midi_file = generated_midi
|
159 |
+
sample_midi = None
|
160 |
+
elif sample_midi:
|
161 |
+
midi_file = sample_midi
|
162 |
+
|
163 |
+
if midi_file:
|
164 |
+
with st.spinner(f"Transcribing to FluidSynth"):
|
165 |
+
midi_data = pretty_midi.PrettyMIDI(midi_file)
|
166 |
+
audio_data = midi_data.fluidsynth()
|
167 |
+
audio_data = np.int16(
|
168 |
+
audio_data / np.max(np.abs(audio_data)) * 32767 * 0.9
|
169 |
+
) # -- Normalize for 16 bit audio https://github.com/jkanner/streamlit-audio/blob/main/helper.py
|
170 |
+
|
171 |
+
virtualfile = io.BytesIO()
|
172 |
+
wavfile.write(virtualfile, 44100, audio_data)
|
173 |
+
|
174 |
+
st.audio(virtualfile)
|
175 |
+
st.markdown("Download the audio by right-clicking on the media player")
|
176 |
+
else:
|
177 |
+
st.markdown("Either generate a new MIDI file, or select one of the samples")
|
projects/07_LLM_Fine_Tuned.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
# from llama_cpp import Llama
|
3 |
+
import re
|
4 |
+
|
5 |
+
st.header('Fine Tuned LLM', divider='green')
|
6 |
+
|
7 |
+
st.markdown("#### What is a LLM?")
|
8 |
+
st.markdown("LLM stands for Large Language Model, which are mathematical models trained to predict the next set of words based on the prompt provided to it.")
|
9 |
+
st.markdown("In this case we are using [Meta's LLama 3.1 (8B) Instruct](https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct) as our base model.")
|
10 |
+
st.markdown("#### What is a fine tuning?")
|
11 |
+
st.markdown("Fine tuning is the processes of tweaking the response of the LLM to particular subset of prompts. Most LLMs are trained on a large corpus of generic data, fine tuning just guides the LLM to a specific use case.")
|
12 |
+
st.markdown("In this case I have fine tuned the base model on [Microsoft's Orca Math Word Problems](https://huggingface.co/datasets/microsoft/orca-math-word-problems-200k), so the chatbot below is more of math assistant. Though it occasionally gets the wrong answer, it is willing to try again. I only fine tuned on 1000 math word problems, but I could try to train on the entire dataset in the future.")
|
13 |
+
st.markdown("#### What is quantization?")
|
14 |
+
st.markdown("Most LLM are quite large, often too large to fit into a computer's memory. So ML developers employ Graphic Processing Units (GPUs) with large amounts of memory to train or make use of such LLMs.")
|
15 |
+
st.markdown("However not everyone has access to such resources, so quantization is the process of decreasing the size of a model so that it can fit into memory (GPU or CPU). The process of quantization decreases the precision used to store the models parameters, at the cost of the model's accuracy.")
|
16 |
+
st.markdown("In this case after I fine tuned the LLama 3.1 (8B) base model, it was quantized to 4 bits which offers reasonable model accuracy at 25% the base model size.")
|
17 |
+
st.markdown("*This is based off the tutorial by Abid Ali Awan [Fine-Tuning Llama 3 and Using It Locally: A Step-by-Step Guide](https://www.datacamp.com/tutorial/llama3-fine-tuning-locally)*")
|
18 |
+
st.divider()
|
19 |
+
|
20 |
+
st.warning("**Work In Progress**")
|
21 |
+
|
22 |
+
if "messages" not in st.session_state:
|
23 |
+
st.session_state["messages"] = [{"role": "system", "content": "You are a helpful math assistant."}, {"role": "assistant", "content": "What math problem can I help you with today?"}]
|
24 |
+
|
25 |
+
def chat_action(prompt):
|
26 |
+
if len(st.session_state.messages) > 6:
|
27 |
+
st.session_state.messages.pop(2)
|
28 |
+
st.session_state.messages.pop(2)
|
29 |
+
st.session_state["messages"].append({"role": "user", "content": prompt})
|
30 |
+
st.chat_message("user").write(prompt)
|
31 |
+
|
32 |
+
# with st.spinner(f"Generating response"):
|
33 |
+
# response = llm.create_chat_completion(
|
34 |
+
# messages=st.session_state.messages,
|
35 |
+
# temperature = 0.7,
|
36 |
+
# repeat_penalty = 1.1,
|
37 |
+
# stop = "[/INST]"
|
38 |
+
# )
|
39 |
+
# msg = response['choices'][0]['message']['content']
|
40 |
+
# msg = re.sub(r'(<<|\[)*(INST|SYS)(>>|\])*', '', msg)
|
41 |
+
# st.session_state["messages"].append({"role": "assistant", "content": msg})
|
42 |
+
# st.chat_message("assistant").write(msg)
|
43 |
+
|
44 |
+
# @st.cache_resource
|
45 |
+
# def load_llm():
|
46 |
+
# #### Import Model from Huggingface
|
47 |
+
# llm = Llama.from_pretrained(
|
48 |
+
# repo_id="ccapo/llama-3.1-8b-chat-math-teacher-GGUF",
|
49 |
+
# filename="*Q4_K_M.gguf",
|
50 |
+
# verbose=False,
|
51 |
+
# n_ctx=2048
|
52 |
+
# )
|
53 |
+
# return llm
|
54 |
+
|
55 |
+
for msg in st.session_state.messages:
|
56 |
+
if msg["role"] != "system":
|
57 |
+
with st.chat_message(name=msg["role"]):
|
58 |
+
st.write(msg["content"])
|
59 |
+
|
60 |
+
# with st.spinner(f"Loading LLM"):
|
61 |
+
# llm = load_llm()
|
62 |
+
|
63 |
+
if prompt := st.chat_input():
|
64 |
+
chat_action(prompt)
|