Spaces:
Sleeping
Sleeping
Upload 6 files
Browse files- .gitignore +165 -0
- README.md +42 -13
- app.py +47 -0
- demo.py +42 -0
- requirements.txt +9 -0
- test_run.py +6 -0
.gitignore
ADDED
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import pipeline
|
2 |
+
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
3 |
+
import re
|
4 |
+
import numpy as np
|
5 |
+
from typing import Dict, List, Tuple
|
6 |
+
|
7 |
+
class GiftRecommender:
|
8 |
+
def __init__(self):
|
9 |
+
# Initialize NER pipeline for entity extraction
|
10 |
+
self.ner = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english")
|
11 |
+
|
12 |
+
# Initialize sentiment analysis pipeline
|
13 |
+
self.sentiment = pipeline("sentiment-analysis")
|
14 |
+
|
15 |
+
# Initialize zero-shot classification for interest categorization
|
16 |
+
self.zero_shot = pipeline("zero-shot-classification")
|
17 |
+
|
18 |
+
# Define interest categories
|
19 |
+
self.interest_categories = [
|
20 |
+
"art", "music", "sports", "technology", "reading",
|
21 |
+
"travel", "cooking", "gaming", "fashion", "outdoor activities"
|
22 |
+
]
|
23 |
+
|
24 |
+
# Initialize gift rules database
|
25 |
+
self.gift_rules = {
|
26 |
+
"art": ["art supplies set", "digital drawing tablet", "museum membership", "art classes"],
|
27 |
+
"music": ["wireless headphones", "concert tickets", "vinyl records", "music streaming subscription"],
|
28 |
+
"sports": ["fitness tracker", "sports equipment", "team merchandise", "gym membership"],
|
29 |
+
"technology": ["smart gadgets", "latest electronics", "tech accessories", "coding courses"],
|
30 |
+
"reading": ["e-reader", "book subscription", "rare book editions", "bookstore gift card"],
|
31 |
+
"travel": ["travel accessories", "language learning subscription", "travel guides", "luggage"],
|
32 |
+
"cooking": ["cooking classes", "premium ingredients set", "kitchen gadgets", "cookbook collection"],
|
33 |
+
"gaming": ["gaming console", "gaming accessories", "game subscription", "collectible items"],
|
34 |
+
"fashion": ["designer accessories", "fashion subscription box", "custom jewelry", "premium clothing"],
|
35 |
+
"outdoor": ["camping gear", "hiking equipment", "outdoor experiences", "adventure gear"]
|
36 |
+
}
|
37 |
+
|
38 |
+
def extract_age(self, text: str) -> int:
|
39 |
+
"""Extract age from text using regex."""
|
40 |
+
age_pattern = r'\b(\d{1,2})\s*-?\s*years?\s*-?\s*old\b|\b(\d{1,2})\b'
|
41 |
+
matches = re.findall(age_pattern, text)
|
42 |
+
if matches:
|
43 |
+
# Return the first number found
|
44 |
+
age = next(int(num) for nums in matches for num in nums if num)
|
45 |
+
return age if 0 < age < 120 else None
|
46 |
+
return None
|
47 |
+
|
48 |
+
def extract_gender(self, text: str) -> str:
|
49 |
+
"""Extract gender from text using keywords."""
|
50 |
+
text = text.lower()
|
51 |
+
gender_indicators = {
|
52 |
+
'female': ['she', 'her', 'sister', 'girlfriend', 'wife', 'daughter', 'mom', 'mother'],
|
53 |
+
'male': ['he', 'him', 'brother', 'boyfriend', 'husband', 'son', 'dad', 'father']
|
54 |
+
}
|
55 |
+
|
56 |
+
for gender, indicators in gender_indicators.items():
|
57 |
+
if any(indicator in text for indicator in indicators):
|
58 |
+
return gender
|
59 |
+
return "unknown"
|
60 |
+
|
61 |
+
def extract_interests(self, text: str) -> List[Dict]:
|
62 |
+
"""Extract and categorize interests using zero-shot classification."""
|
63 |
+
# First, extract potential interest phrases
|
64 |
+
interest_pattern = r'loves?\s+([^,.]+)|\blikes?\s+([^,.]+)'
|
65 |
+
matches = re.findall(interest_pattern, text.lower())
|
66 |
+
|
67 |
+
interests = []
|
68 |
+
for match in matches:
|
69 |
+
phrase = next(m for m in match if m)
|
70 |
+
# Classify the interest into predefined categories
|
71 |
+
result = self.zero_shot(
|
72 |
+
phrase,
|
73 |
+
candidate_labels=self.interest_categories,
|
74 |
+
multi_label=False
|
75 |
+
)
|
76 |
+
|
77 |
+
# Get sentiment score for the interest
|
78 |
+
sentiment_score = self.sentiment(phrase)[0]
|
79 |
+
|
80 |
+
interests.append({
|
81 |
+
'phrase': phrase,
|
82 |
+
'category': result['labels'][0],
|
83 |
+
'confidence': result['scores'][0],
|
84 |
+
'sentiment': sentiment_score['label'],
|
85 |
+
'sentiment_score': sentiment_score['score']
|
86 |
+
})
|
87 |
+
|
88 |
+
return interests
|
89 |
+
|
90 |
+
def extract_dislikes(self, text: str) -> List[str]:
|
91 |
+
"""Extract dislikes from text."""
|
92 |
+
dislike_pattern = r'hates?\s+([^,.]+)|dislikes?\s+([^,.]+)'
|
93 |
+
matches = re.findall(dislike_pattern, text.lower())
|
94 |
+
return [next(m for m in match if m) for match in matches]
|
95 |
+
|
96 |
+
def get_gift_recommendations(self, text: str) -> Dict:
|
97 |
+
"""Process text and return gift recommendations."""
|
98 |
+
# Extract basic information
|
99 |
+
age = self.extract_age(text)
|
100 |
+
gender = self.extract_gender(text)
|
101 |
+
interests = self.extract_interests(text)
|
102 |
+
dislikes = self.extract_dislikes(text)
|
103 |
+
|
104 |
+
# Generate recommendations based on interests
|
105 |
+
recommendations = []
|
106 |
+
for interest in interests:
|
107 |
+
category = interest['category']
|
108 |
+
if category in self.gift_rules:
|
109 |
+
# Weight recommendations by sentiment and confidence
|
110 |
+
weight = interest['confidence'] * (2 if interest['sentiment'] == 'POSITIVE' else 1)
|
111 |
+
recommendations.extend([
|
112 |
+
{
|
113 |
+
'gift': gift,
|
114 |
+
'category': category,
|
115 |
+
'weight': weight,
|
116 |
+
'reason': f"Based on their interest in {interest['phrase']}"
|
117 |
+
}
|
118 |
+
for gift in self.gift_rules[category]
|
119 |
+
])
|
120 |
+
|
121 |
+
# Sort recommendations by weight
|
122 |
+
recommendations.sort(key=lambda x: x['weight'], reverse=True)
|
123 |
+
|
124 |
+
return {
|
125 |
+
'profile': {
|
126 |
+
'age': age,
|
127 |
+
'gender': gender,
|
128 |
+
'interests': interests,
|
129 |
+
'dislikes': dislikes
|
130 |
+
},
|
131 |
+
'recommendations': recommendations[:5] # Return top 5 recommendations
|
132 |
+
}
|
133 |
+
|
134 |
+
def format_recommendations(self, results: Dict) -> str:
|
135 |
+
"""Format the recommendations into a readable string."""
|
136 |
+
output = []
|
137 |
+
output.append("π Gift Recommendations\n")
|
138 |
+
|
139 |
+
profile = results['profile']
|
140 |
+
output.append(f"Profile Summary:")
|
141 |
+
output.append(f"- Age: {profile['age'] or 'Unknown'}")
|
142 |
+
output.append(f"- Gender: {profile['gender'].title()}")
|
143 |
+
output.append("- Interests: " + ", ".join(i['phrase'] for i in profile['interests']))
|
144 |
+
if profile['dislikes']:
|
145 |
+
output.append("- Dislikes: " + ", ".join(profile['dislikes']))
|
146 |
+
|
147 |
+
output.append("\nTop Recommendations:")
|
148 |
+
for i, rec in enumerate(results['recommendations'], 1):
|
149 |
+
output.append(f"{i}. {rec['gift']}")
|
150 |
+
output.append(f" β’ {rec['reason']}")
|
151 |
+
|
152 |
+
return "\n".join(output)
|
153 |
+
|
154 |
+
# Example usage
|
155 |
+
if __name__ == "__main__":
|
156 |
+
recommender = GiftRecommender()
|
157 |
+
|
158 |
+
# Example input
|
159 |
+
text = """I'm looking for a gift for my 25-year-old sister.
|
160 |
+
She loves painting and traveling, especially in Japan.
|
161 |
+
She hates loud noises and doesn't like spicy food."""
|
162 |
+
|
163 |
+
results = recommender.get_gift_recommendations(text)
|
164 |
+
formatted_output = recommender.format_recommendations(results)
|
165 |
+
print(formatted_output)
|
README.md
CHANGED
@@ -1,13 +1,42 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# NLP Gift Recommender
|
2 |
+
|
3 |
+
An NLP-powered gift recommendation system using Hugging Face transformers.
|
4 |
+
|
5 |
+
## Setup
|
6 |
+
|
7 |
+
1. Clone the repository:
|
8 |
+
```bash
|
9 |
+
git clone https://github.com/yourusername/nlp-gift-recommender.git
|
10 |
+
cd nlp-gift-recommender
|
11 |
+
```
|
12 |
+
|
13 |
+
2. Install dependencies:
|
14 |
+
```bash
|
15 |
+
pip install -r requirements.txt
|
16 |
+
```
|
17 |
+
|
18 |
+
## Usage
|
19 |
+
|
20 |
+
```python
|
21 |
+
from src.recommender import GiftRecommender
|
22 |
+
|
23 |
+
recommender = GiftRecommender()
|
24 |
+
text = "I'm looking for a gift for my 25-year-old sister who loves painting."
|
25 |
+
recommendations = recommender.get_gift_recommendations(text)
|
26 |
+
print(recommender.format_recommendations(recommendations))
|
27 |
+
```
|
28 |
+
|
29 |
+
## Features
|
30 |
+
|
31 |
+
- Natural language processing for gift preferences
|
32 |
+
- Sentiment analysis for interest weighting
|
33 |
+
- Customizable gift categories and rules
|
34 |
+
- Detailed recommendation explanations
|
35 |
+
|
36 |
+
## Contributing
|
37 |
+
|
38 |
+
1. Fork the repository
|
39 |
+
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
|
40 |
+
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
41 |
+
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
42 |
+
5. Open a Pull Request
|
app.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import requests
|
3 |
+
from transformers import pipeline
|
4 |
+
|
5 |
+
# Load NLP model
|
6 |
+
zero_shot = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
|
7 |
+
|
8 |
+
# π Web search for gift suggestions
|
9 |
+
def search_gifts(query):
|
10 |
+
amazon_url = f"https://www.amazon.in/s?k={query.replace(' ', '+')}"
|
11 |
+
igp_url = f"https://www.igp.com/search?q={query.replace(' ', '+')}"
|
12 |
+
indiamart_url = f"https://dir.indiamart.com/search.mp?ss={query.replace(' ', '+')}"
|
13 |
+
|
14 |
+
return {"Amazon": amazon_url, "IGP": igp_url, "IndiaMart": indiamart_url}
|
15 |
+
|
16 |
+
# π― Main function for gift recommendation
|
17 |
+
def recommend_gifts(text):
|
18 |
+
if not text:
|
19 |
+
return "Please enter a description."
|
20 |
+
|
21 |
+
# NLP Processing
|
22 |
+
categories = ["art", "music", "tech", "travel", "books", "fashion", "fitness", "gaming"]
|
23 |
+
results = zero_shot(text, categories)
|
24 |
+
|
25 |
+
# Get top interest
|
26 |
+
top_interest = results["labels"][0]
|
27 |
+
|
28 |
+
# Search for gifts based on interest
|
29 |
+
links = search_gifts(top_interest)
|
30 |
+
|
31 |
+
return {
|
32 |
+
"Predicted Interest": top_interest,
|
33 |
+
"Gift Suggestions": links
|
34 |
+
}
|
35 |
+
|
36 |
+
# π¨ Gradio UI for easy interaction
|
37 |
+
demo = gr.Interface(
|
38 |
+
fn=recommend_gifts,
|
39 |
+
inputs="text",
|
40 |
+
outputs="json",
|
41 |
+
title="π AI Gift Recommender",
|
42 |
+
description="Enter details about the person you are buying a gift for, and get personalized suggestions with shopping links!",
|
43 |
+
)
|
44 |
+
|
45 |
+
# π Launch Gradio App
|
46 |
+
if __name__ == "__main__":
|
47 |
+
demo.launch()
|
demo.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from src.recommender import GiftRecommender
|
2 |
+
|
3 |
+
def main():
|
4 |
+
recommender = GiftRecommender()
|
5 |
+
print("\nπ Gift Recommendation System")
|
6 |
+
print("-" * 30)
|
7 |
+
print("Example: '25-year-old sister who loves painting and traveling'")
|
8 |
+
print("Or simply enter an interest like 'games' or 'sports'.")
|
9 |
+
|
10 |
+
while True:
|
11 |
+
try:
|
12 |
+
text = input("\nWho are you shopping for? ('quit' to exit): ").strip()
|
13 |
+
if text.lower() == 'quit':
|
14 |
+
break
|
15 |
+
|
16 |
+
if len(text) < 3:
|
17 |
+
print("β οΈ Please provide more details or a valid interest.")
|
18 |
+
continue
|
19 |
+
|
20 |
+
# Get recommendations
|
21 |
+
results = recommender.get_gift_recommendations(text)
|
22 |
+
|
23 |
+
# If no structured interests found, assume the input itself is an interest
|
24 |
+
if not results['profile']['interests']:
|
25 |
+
print(f"\nπ No specific interests found, assuming '{text}' is the interest.")
|
26 |
+
results['profile']['interests'].append({'phrase': text, 'category': text, 'confidence': 1.0, 'sentiment': 'POSITIVE', 'sentiment_score': 1.0})
|
27 |
+
|
28 |
+
# Fetch new recommendations based on this interest
|
29 |
+
results['recommendations'] = recommender.generate_gift_ideas(text)
|
30 |
+
|
31 |
+
print("\n" + recommender.format_recommendations(results))
|
32 |
+
|
33 |
+
print("\nπ Suggested Gifts:")
|
34 |
+
for i, rec in enumerate(results['recommendations'], 1):
|
35 |
+
print(f"{i}. {rec['gift']}")
|
36 |
+
|
37 |
+
except Exception as e:
|
38 |
+
print(f"\nβ Error: {str(e)}")
|
39 |
+
print("π Please try again with different wording.")
|
40 |
+
|
41 |
+
if __name__ == "__main__":
|
42 |
+
main()
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
flask
|
2 |
+
transformers>=4.30.0
|
3 |
+
torch>=2.0.0
|
4 |
+
numpy>=1.24.0
|
5 |
+
pytest>=7.3.1
|
6 |
+
gunicorn
|
7 |
+
requests
|
8 |
+
sentence-transformers
|
9 |
+
gradio
|
test_run.py
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from src.recommender import GiftRecommender
|
2 |
+
|
3 |
+
recommender = GiftRecommender()
|
4 |
+
text = "I'm looking for a gift for my 25-year-old sister who loves painting."
|
5 |
+
results = recommender.get_gift_recommendations(text)
|
6 |
+
print(recommender.format_recommendations(results))
|